Line data Source code
1 : /********************************************************************\
2 :
3 : Name: mjsonrpc.cxx
4 : Created by: Konstantin Olchanski
5 :
6 : Contents: handler of MIDAS standard JSON-RPC requests
7 :
8 : \********************************************************************/
9 :
10 : #undef NDEBUG // midas required assert() to be always enabled
11 :
12 : #include <stdio.h>
13 : #include <stdlib.h>
14 : #include <assert.h>
15 : #include <map>
16 :
17 : #include "mjson.h"
18 : #include "midas.h"
19 : #include "msystem.h"
20 : #include "mstrlcpy.h"
21 :
22 : #include "mjsonrpc.h"
23 :
24 : #include <mutex> // std::mutex
25 :
26 : //////////////////////////////////////////////////////////////////////
27 : //
28 : // Specifications for JSON-RPC
29 : //
30 : // https://tools.ietf.org/html/rfc4627 - JSON RFC
31 : // http://www.jsonrpc.org/specification - specification of JSON-RPC 2.0
32 : // http://www.simple-is-better.org/json-rpc/transport_http.html
33 : //
34 : // NB - MIDAS JSON (odb.c and mjson.cxx) encode IEEE754/854 numeric values
35 : // NaN and +/-Inf into JSON strings "NaN", "Infinity" and "-Infinity"
36 : // for reasons unknown, the JSON standard does not specify a standard
37 : // way for encoding these numeric values.
38 : //
39 : // NB - Batch requests are processed in order and the returned array of responses
40 : // has the resonses in exactly same order as the requests for simpler
41 : // matching of requests and responses - 1st response to 1st request,
42 : // 2nd response to 2nd request and so forth.
43 : //
44 : //////////////////////////////////////////////////////////////////////
45 : //
46 : // JSON-RPC error codes:
47 : //
48 : // -32700 Parse error Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.
49 : // -32600 Invalid Request The JSON sent is not a valid Request object.
50 : // -32601 Method not found The method does not exist / is not available.
51 : // -32602 Invalid params Invalid method parameter(s).
52 : // -32603 Internal error Internal JSON-RPC error.
53 : // -32000 to -32099 Server error Reserved for implementation-defined server-errors.
54 : //
55 : // Typical JSON-RPC request:
56 : //
57 : // {
58 : // "jsonrpc": "2.0",
59 : // "method": "sum",
60 : // "params": { "b": 34, "c": 56, "a": 12 },
61 : // "id": 123
62 : // }
63 : //
64 : // Typical JSON-RPC reply:
65 : //
66 : // {
67 : // "jsonrpc": "2.0",
68 : // "result": 102,
69 : // "id": 5
70 : // }
71 : //
72 : // Typical error reply:
73 : //
74 : // {
75 : // "jsonrpc": "2.0",
76 : // "error": {
77 : // "code": -32600,
78 : // "message": "Invalid Request.",
79 : // "data": "'method' is missing"
80 : // },
81 : // "id": 6
82 : // }
83 : // }
84 : //
85 : //////////////////////////////////////////////////////////////////////
86 : //
87 : // JSON-RPC is documented via an automatically generated JSON Schema.
88 : //
89 : // For more information about JSON Schemas, see:
90 : //
91 : // https://tools.ietf.org/html/draft-zyp-json-schema-04
92 : // http://spacetelescope.github.io/understanding-json-schema/
93 : // http://json-schema.org/
94 : //
95 : // JSON Schema examples:
96 : // http://json-schema.org/examples.html
97 : // http://json-schema.org/example1.html
98 : //
99 : // JSON Schema visualization: (schema file has to have a .json extension)
100 : // https://github.com/lbovet/docson
101 : //
102 : // Non-standard proposed JSON-RPC schema is *NOT* used: (no visualization tools)
103 : // http://www.simple-is-better.org/json-rpc/jsonrpc20-schema-service-descriptor.html
104 : //
105 : // Variances of MIDAS JSON-RPC Schema from standard:
106 : //
107 : // - optional parameters end with "?" and have an "optional:true" attribute, i.e. "bUnique?"
108 : // - array parameters end with "[]", JSON Schema array schema is not generated yet
109 : //
110 : //////////////////////////////////////////////////////////////////////
111 :
112 : int mjsonrpc_debug = 0; // in mjsonrpc.h
113 : static int mjsonrpc_sleep = 0;
114 : static int mjsonrpc_time = 0;
115 :
116 0 : static double GetTimeSec()
117 : {
118 : struct timeval tv;
119 0 : gettimeofday(&tv, NULL);
120 0 : return tv.tv_sec*1.0 + tv.tv_usec/1000000.0;
121 : }
122 :
123 0 : MJsonNode* mjsonrpc_make_error(int code, const char* message, const char* data)
124 : {
125 0 : MJsonNode* errnode = MJsonNode::MakeObject();
126 0 : errnode->AddToObject("code", MJsonNode::MakeInt(code));
127 0 : errnode->AddToObject("message", MJsonNode::MakeString(message));
128 0 : errnode->AddToObject("data", MJsonNode::MakeString(data));
129 :
130 0 : MJsonNode* result = MJsonNode::MakeObject();
131 0 : result->AddToObject("error", errnode);
132 0 : return result;
133 : }
134 :
135 0 : MJsonNode* mjsonrpc_make_result(MJsonNode* node)
136 : {
137 0 : MJsonNode* result = MJsonNode::MakeObject();
138 0 : result->AddToObject("result", node);
139 0 : return result;
140 : }
141 :
142 0 : MJsonNode* mjsonrpc_make_result(const char* name, MJsonNode* value, const char* name2, MJsonNode* value2, const char* name3, MJsonNode* value3)
143 : {
144 0 : MJsonNode* node = MJsonNode::MakeObject();
145 :
146 0 : if (name)
147 0 : node->AddToObject(name, value);
148 0 : if (name2)
149 0 : node->AddToObject(name2, value2);
150 0 : if (name3)
151 0 : node->AddToObject(name3, value3);
152 :
153 0 : MJsonNode* result = MJsonNode::MakeObject();
154 0 : result->AddToObject("result", node);
155 0 : return result;
156 : }
157 :
158 0 : MJsonNode* mjsonrpc_make_result(const char* name, MJsonNode* value, const char* name2, MJsonNode* value2, const char* name3, MJsonNode* value3, const char* name4, MJsonNode* value4)
159 : {
160 0 : MJsonNode* node = MJsonNode::MakeObject();
161 :
162 0 : if (name)
163 0 : node->AddToObject(name, value);
164 0 : if (name2)
165 0 : node->AddToObject(name2, value2);
166 0 : if (name3)
167 0 : node->AddToObject(name3, value3);
168 0 : if (name4)
169 0 : node->AddToObject(name4, value4);
170 :
171 0 : MJsonNode* result = MJsonNode::MakeObject();
172 0 : result->AddToObject("result", node);
173 0 : return result;
174 : }
175 :
176 : static MJsonNode* gNullNode = NULL;
177 :
178 0 : const MJsonNode* mjsonrpc_get_param(const MJsonNode* params, const char* name, MJsonNode** error)
179 : {
180 0 : assert(gNullNode != NULL);
181 :
182 : // NULL params is a request for documentation, return an empty object
183 0 : if (!params) {
184 0 : if (error)
185 0 : *error = MJsonNode::MakeObject();
186 0 : return gNullNode;
187 : }
188 :
189 0 : const MJsonNode* obj = params->FindObjectNode(name);
190 0 : if (!obj) {
191 0 : if (error)
192 0 : *error = mjsonrpc_make_error(-32602, "Invalid params", (std::string("missing parameter: ") + name).c_str());
193 0 : return gNullNode;
194 : }
195 :
196 0 : if (error)
197 0 : *error = NULL;
198 0 : return obj;
199 : }
200 :
201 0 : const MJsonNodeVector* mjsonrpc_get_param_array(const MJsonNode* params, const char* name, MJsonNode** error)
202 : {
203 : // NULL params is a request for documentation, return NULL
204 0 : if (!params) {
205 0 : if (error)
206 0 : *error = MJsonNode::MakeObject();
207 0 : return NULL;
208 : }
209 :
210 0 : const MJsonNode* node = mjsonrpc_get_param(params, name, error);
211 :
212 : // handle error return from mjsonrpc_get_param()
213 0 : if (error && *error) {
214 0 : return NULL;
215 : }
216 :
217 0 : const MJsonNodeVector* v = node->GetArray();
218 :
219 0 : if (!v) {
220 0 : if (error)
221 0 : *error = mjsonrpc_make_error(-32602, "Invalid params", (std::string("parameter must be an array: ") + name).c_str());
222 0 : return NULL;
223 : }
224 :
225 0 : if (error)
226 0 : *error = NULL;
227 0 : return v;
228 : }
229 :
230 0 : MJSO* MJSO::MakeObjectSchema(const char* description) // constructor for object schema
231 : {
232 0 : MJSO* p = new MJSO();
233 0 : if (description)
234 0 : p->AddToObject("description", MJsonNode::MakeString(description));
235 0 : p->AddToObject("type", MJsonNode::MakeString("object"));
236 0 : p->properties = MJsonNode::MakeObject();
237 0 : p->required = MJsonNode::MakeArray();
238 0 : p->AddToObject("properties", p->properties);
239 0 : p->AddToObject("required", p->required);
240 0 : return p;
241 : }
242 :
243 0 : MJSO* MJSO::MakeArraySchema(const char* description) // constructor for array schema
244 : {
245 0 : MJSO* p = new MJSO();
246 0 : p->AddToObject("description", MJsonNode::MakeString(description));
247 0 : p->AddToObject("type", MJsonNode::MakeString("array"));
248 0 : p->items = MJsonNode::MakeArray();
249 0 : p->AddToObject("items", p->items);
250 0 : return p;
251 : }
252 :
253 0 : static std::string remove(const std::string s, char c)
254 : {
255 0 : std::string::size_type pos = s.find(c);
256 0 : if (pos == std::string::npos)
257 0 : return s;
258 : else
259 0 : return s.substr(0, pos);
260 : }
261 :
262 0 : void MJSO::AddToSchema(MJsonNode* s, const char* xname)
263 : {
264 0 : if (!xname)
265 0 : xname = "";
266 :
267 0 : bool optional = strchr(xname, '?');
268 0 : bool array = strchr(xname, '[');
269 :
270 : // remove the "?" and "[]" marker characters
271 0 : std::string name = xname;
272 0 : name = remove(name, '?');
273 0 : name = remove(name, '[');
274 0 : name = remove(name, ']');
275 :
276 0 : if (optional)
277 0 : s->AddToObject("optional", MJsonNode::MakeBool(true));
278 :
279 0 : if (array) { // insert an array schema
280 0 : MJSO* ss = MakeArraySchema(s->FindObjectNode("description")->GetString().c_str());
281 0 : s->DeleteObjectNode("description");
282 0 : ss->AddToSchema(s, "");
283 0 : s = ss;
284 : }
285 :
286 0 : if (items)
287 0 : items->AddToArray(s);
288 : else {
289 0 : assert(properties);
290 0 : assert(required);
291 0 : properties->AddToObject(name.c_str(), s);
292 0 : if (!optional) {
293 0 : required->AddToArray(MJsonNode::MakeString(name.c_str()));
294 : }
295 : }
296 0 : }
297 :
298 0 : MJSO* MJSO::I()
299 : {
300 0 : return MakeObjectSchema(NULL);
301 : }
302 :
303 0 : void MJSO::D(const char* description)
304 : {
305 0 : this->AddToObject("description", MJsonNode::MakeString(description));
306 0 : }
307 :
308 0 : MJSO* MJSO::Params()
309 : {
310 0 : if (!params) {
311 0 : params = MakeObjectSchema(NULL);
312 0 : this->AddToSchema(params, "params");
313 : }
314 0 : return params;
315 : }
316 :
317 0 : MJSO* MJSO::Result()
318 : {
319 0 : if (!result) {
320 0 : result = MakeObjectSchema(NULL);
321 0 : this->AddToSchema(result, "result");
322 : }
323 0 : return result;
324 : }
325 :
326 0 : MJSO* MJSO::PA(const char* description)
327 : {
328 0 : MJSO* s = MakeArraySchema(description);
329 0 : this->AddToSchema(s, "params");
330 0 : return s;
331 : }
332 :
333 0 : MJSO* MJSO::RA(const char* description)
334 : {
335 0 : MJSO* s = MakeArraySchema(description);
336 0 : this->AddToSchema(s, "result");
337 0 : return s;
338 : }
339 :
340 0 : void MJSO::P(const char* name, int mjson_type, const char* description)
341 : {
342 0 : if (name == NULL)
343 0 : this->Add("params", mjson_type, description);
344 : else
345 0 : Params()->Add(name, mjson_type, description);
346 0 : }
347 :
348 0 : void MJSO::R(const char* name, int mjson_type, const char* description)
349 : {
350 0 : if (name == NULL)
351 0 : this->Add("result", mjson_type, description);
352 : else
353 0 : Result()->Add(name, mjson_type, description);
354 0 : }
355 :
356 0 : void MJSO::Add(const char* name, int mjson_type, const char* description)
357 : {
358 0 : MJsonNode* p = MJsonNode::MakeObject();
359 0 : p->AddToObject("description", MJsonNode::MakeString(description));
360 0 : if (mjson_type == MJSON_ARRAY)
361 0 : p->AddToObject("type", MJsonNode::MakeString("array"));
362 0 : else if (mjson_type == MJSON_OBJECT)
363 0 : p->AddToObject("type", MJsonNode::MakeString("object"));
364 0 : else if (mjson_type == MJSON_STRING)
365 0 : p->AddToObject("type", MJsonNode::MakeString("string"));
366 0 : else if (mjson_type == MJSON_INT)
367 0 : p->AddToObject("type", MJsonNode::MakeString("integer"));
368 0 : else if (mjson_type == MJSON_NUMBER)
369 0 : p->AddToObject("type", MJsonNode::MakeString("number"));
370 0 : else if (mjson_type == MJSON_BOOL)
371 0 : p->AddToObject("type", MJsonNode::MakeString("bool"));
372 0 : else if (mjson_type == MJSON_NULL)
373 0 : p->AddToObject("type", MJsonNode::MakeString("null"));
374 0 : else if (mjson_type == MJSON_ARRAYBUFFER)
375 0 : p->AddToObject("type", MJsonNode::MakeString("arraybuffer"));
376 0 : else if (mjson_type == MJSON_JSON)
377 0 : p->AddToObject("type", MJsonNode::MakeString("json"));
378 0 : else if (mjson_type == 0)
379 : ;
380 : else
381 0 : assert(!"invalid value of mjson_type");
382 0 : this->AddToSchema(p, name);
383 0 : }
384 :
385 0 : MJSO* MJSO::AddObject(const char* name, const char* description)
386 : {
387 0 : MJSO* s = MakeObjectSchema(description);
388 0 : s->AddToObject("description", MJsonNode::MakeString(description));
389 0 : s->AddToObject("type", MJsonNode::MakeString("object"));
390 0 : this->AddToSchema(s, name);
391 0 : return s;
392 : }
393 :
394 0 : MJSO* MJSO::AddArray(const char* name, const char* description)
395 : {
396 0 : MJSO* s = MakeArraySchema(description);
397 0 : s->AddToObject("description", MJsonNode::MakeString(description));
398 0 : s->AddToObject("type", MJsonNode::MakeString("array"));
399 0 : this->AddToSchema(s, name);
400 0 : return s;
401 : }
402 :
403 0 : MJSO::MJSO() // ctor
404 0 : : MJsonNode(MJSON_OBJECT)
405 : {
406 0 : properties = NULL;
407 0 : required = NULL;
408 0 : items = NULL;
409 0 : params = NULL;
410 0 : result = NULL;
411 0 : }
412 :
413 0 : static MJsonNode* xnull(const MJsonNode* params)
414 : {
415 0 : if (!params) {
416 0 : MJSO* doc = MJSO::I();
417 0 : doc->D("RPC method always returns null");
418 0 : doc->P(NULL, 0, "method parameters are ignored");
419 0 : doc->R(NULL, MJSON_NULL, "always returns null");
420 0 : return doc;
421 : }
422 :
423 0 : return mjsonrpc_make_result(MJsonNode::MakeNull());
424 : }
425 :
426 : /////////////////////////////////////////////////////////////////////////////////
427 : //
428 : // Programs start/stop code goes here
429 : //
430 : /////////////////////////////////////////////////////////////////////////////////
431 :
432 0 : static MJsonNode* js_cm_exist(const MJsonNode* params)
433 : {
434 0 : if (!params) {
435 0 : MJSO* doc = MJSO::I();
436 0 : doc->D("calls MIDAS cm_exist() to check if given MIDAS program is running");
437 0 : doc->P("name", MJSON_STRING, "name of the program, corresponding to ODB /Programs/name");
438 0 : doc->P("unique?", MJSON_BOOL, "bUnique argument to cm_exist()");
439 0 : doc->R("status", MJSON_INT, "return status of cm_exist()");
440 0 : return doc;
441 : }
442 :
443 0 : MJsonNode* error = NULL;
444 :
445 0 : std::string name = mjsonrpc_get_param(params, "name", &error)->GetString();
446 0 : if (error)
447 0 : return error;
448 :
449 0 : int unique = mjsonrpc_get_param(params, "unique", NULL)->GetBool();
450 :
451 0 : int status = cm_exist(name.c_str(), unique);
452 :
453 0 : if (mjsonrpc_debug)
454 0 : printf("cm_exist(%s,%d) -> %d\n", name.c_str(), unique, status);
455 :
456 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
457 0 : }
458 :
459 0 : static MJsonNode* js_cm_shutdown(const MJsonNode* params)
460 : {
461 0 : if (!params) {
462 0 : MJSO *doc = MJSO::I();
463 0 : doc->D("calls MIDAS cm_shutdown() to stop given MIDAS program");
464 0 : doc->P("name", MJSON_STRING, "name of the program, corresponding to ODB /Programs/name");
465 0 : doc->P("unique?", MJSON_BOOL, "bUnique argument to cm_shutdown()");
466 0 : doc->R("status", MJSON_INT, "return status of cm_shutdown()");
467 0 : return doc;
468 : }
469 :
470 0 : MJsonNode* error = NULL;
471 :
472 0 : std::string name = mjsonrpc_get_param(params, "name", &error)->GetString();
473 0 : if (error)
474 0 : return error;
475 :
476 0 : int unique = mjsonrpc_get_param(params, "unique", NULL)->GetBool();
477 :
478 0 : int status = cm_shutdown(name.c_str(), unique);
479 :
480 0 : if (mjsonrpc_debug)
481 0 : printf("cm_shutdown(%s,%d) -> %d\n", name.c_str(), unique, status);
482 :
483 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
484 0 : }
485 :
486 0 : static MJsonNode* start_program(const MJsonNode* params)
487 : {
488 0 : if (!params) {
489 0 : MJSO* doc = MJSO::I();
490 0 : doc->D("start MIDAS program defined in ODB /Programs/name");
491 0 : doc->P("name", MJSON_STRING, "name of the program, corresponding to ODB /Programs/name");
492 0 : doc->R("status", MJSON_INT, "return status of ss_system()");
493 0 : return doc;
494 : }
495 :
496 0 : MJsonNode* error = NULL;
497 :
498 0 : std::string name = mjsonrpc_get_param(params, "name", &error)->GetString(); if (error) return error;
499 :
500 0 : std::string path = "";
501 0 : path += "/Programs/";
502 0 : path += name;
503 0 : path += "/Start command";
504 :
505 : HNDLE hDB;
506 0 : cm_get_experiment_database(&hDB, NULL);
507 :
508 : char command[256];
509 0 : int size = sizeof(command);
510 0 : int status = db_get_value(hDB, 0, path.c_str(), command, &size, TID_STRING, FALSE);
511 :
512 0 : if (status == DB_SUCCESS && command[0]) {
513 0 : status = ss_system(command);
514 : }
515 :
516 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
517 0 : }
518 :
519 0 : static MJsonNode* exec_script(const MJsonNode* params)
520 : {
521 0 : if (!params) {
522 0 : MJSO* doc = MJSO::I();
523 0 : doc->D("execute custom script defined in ODB /Script (scripts show in the menu) or /CustomScript (scripts from custom pages)");
524 0 : doc->P("script?", MJSON_STRING, "Execute ODB /Script/xxx");
525 0 : doc->P("customscript?", MJSON_STRING, "Execute ODB /CustomScript/xxx");
526 0 : doc->R("status", MJSON_INT, "return status of cm_exec_script()");
527 0 : return doc;
528 : }
529 :
530 0 : std::string script = mjsonrpc_get_param(params, "script", NULL)->GetString();
531 0 : std::string customscript = mjsonrpc_get_param(params, "customscript", NULL)->GetString();
532 :
533 0 : std::string path;
534 :
535 0 : if (script.length() > 0) {
536 0 : path += "/Script";
537 0 : path += "/";
538 0 : path += script;
539 0 : } else if (customscript.length() > 0) {
540 0 : path += "/CustomScript";
541 0 : path += "/";
542 0 : path += customscript;
543 : }
544 :
545 0 : int status = 0;
546 :
547 0 : if (path.length() > 0) {
548 0 : status = cm_exec_script(path.c_str());
549 : }
550 :
551 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
552 0 : }
553 :
554 : /////////////////////////////////////////////////////////////////////////////////
555 : //
556 : // ODB code goes here
557 : //
558 : /////////////////////////////////////////////////////////////////////////////////
559 :
560 0 : static int parse_array_index_list(const char* method, const char* path, std::vector<unsigned> *list)
561 : {
562 : // parse array index in form of:
563 : // odbpath[number]
564 : // odbpath[number,number]
565 : // odbpath[number-number]
566 : // or any combination of them, i.e. odbpath[1,10-15,20,30-40]
567 :
568 0 : const char*s = strchr(path, '[');
569 :
570 0 : if (!s) {
571 0 : cm_msg(MERROR, method, "expected an array index character \'[\' in \"%s\"", path);
572 0 : return DB_OUT_OF_RANGE;
573 : }
574 :
575 0 : s++; // skip '[' itself
576 :
577 0 : while (s && (*s != 0)) {
578 :
579 : // check that we have a number
580 0 : if (!isdigit(*s)) {
581 0 : cm_msg(MERROR, method, "expected a number in array index in \"%s\" at \"%s\"", path, s);
582 0 : return DB_OUT_OF_RANGE;
583 : }
584 :
585 0 : unsigned value1 = strtoul(s, (char**)&s, 10);
586 :
587 : // array range,
588 0 : if (*s == '-') {
589 0 : s++; // skip the minus char
590 :
591 0 : if (!isdigit(*s)) {
592 0 : cm_msg(MERROR, method, "expected a number in array index in \"%s\" at \"%s\"", path, s);
593 0 : return DB_OUT_OF_RANGE;
594 : }
595 :
596 0 : unsigned value2 = strtoul(s, (char**)&s, 10);
597 :
598 0 : if (value2 >= value1)
599 0 : for (unsigned i=value1; i<=value2; i++)
600 0 : list->push_back(i);
601 : else {
602 : // this is stupid. simple loop like this
603 : // for (unsigned i=value1; i>=value2; i--)
604 : // does not work for range 4-0, because value2 is 0,
605 : // and x>=0 is always true for unsigned numbers,
606 : // so we would loop forever... K.O.
607 0 : for (unsigned i=value1; i!=value2; i--)
608 0 : list->push_back(i);
609 0 : list->push_back(value2);
610 : }
611 : } else {
612 0 : list->push_back(value1);
613 : }
614 :
615 0 : if (*s == ',') {
616 0 : s++; // skip the comma char
617 0 : continue; // back to the begin of loop
618 : }
619 :
620 0 : if (*s == ']') {
621 0 : s++; // skip the closing bracket
622 0 : s = NULL;
623 0 : continue; // done
624 : }
625 :
626 0 : cm_msg(MERROR, method, "invalid char in array index in \"%s\" at \"%s\"", path, s);
627 0 : return DB_OUT_OF_RANGE;
628 : }
629 :
630 : #if 0
631 : printf("parsed array indices for \"%s\" size is %d: ", path, (int)list->size());
632 : for (unsigned i=0; i<list->size(); i++)
633 : printf(" %d", (*list)[i]);
634 : printf("\n");
635 : #endif
636 :
637 0 : return SUCCESS;
638 : }
639 :
640 0 : static MJsonNode* js_db_get_values(const MJsonNode* params)
641 : {
642 0 : if (!params) {
643 0 : MJSO* doc = MJSO::I();
644 0 : doc->D("get values of ODB data from given subtrees");
645 0 : doc->P("paths[]", MJSON_STRING, "array of ODB subtree paths, see note on array indices");
646 0 : doc->P("omit_names?", MJSON_BOOL, "omit the /name entries");
647 0 : doc->P("omit_last_written?", MJSON_BOOL, "omit the /last_written entries and the last_written[] result");
648 0 : doc->P("omit_tid?", MJSON_BOOL, "omit the tid[] result");
649 0 : doc->P("omit_old_timestamp?", MJSON_NUMBER, "omit data older than given ODB timestamp");
650 0 : doc->P("preserve_case?", MJSON_BOOL, "preserve the capitalization of ODB key names (WARNING: ODB is not case sensitive); note that this will also have side effect of setting the omit_names option");
651 0 : doc->R("data[]", 0, "values of ODB data for each path, all key names are in lower case, all symlinks are followed");
652 0 : doc->R("status[]", MJSON_INT, "return status of db_copy_json_values() or db_copy_json_index() for each path");
653 0 : doc->R("tid?[]", MJSON_INT, "odb type id for each path, absent if omit_tid is true");
654 0 : doc->R("last_written?[]", MJSON_NUMBER, "last_written value of the ODB subtree for each path, absent if omit_last_written is true");
655 0 : return doc;
656 : }
657 :
658 0 : MJsonNode* error = NULL;
659 :
660 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
661 :
662 0 : bool omit_names = mjsonrpc_get_param(params, "omit_names", NULL)->GetBool();
663 0 : bool omit_last_written = mjsonrpc_get_param(params, "omit_last_written", NULL)->GetBool();
664 0 : bool omit_tid = mjsonrpc_get_param(params, "omit_tid", NULL)->GetBool();
665 0 : double xomit_old_timestamp = mjsonrpc_get_param(params, "omit_old_timestamp", NULL)->GetDouble();
666 0 : time_t omit_old_timestamp = (time_t)xomit_old_timestamp;
667 0 : bool preserve_case = mjsonrpc_get_param(params, "preserve_case", NULL)->GetBool();
668 :
669 0 : MJsonNode* dresult = MJsonNode::MakeArray();
670 0 : MJsonNode* sresult = MJsonNode::MakeArray();
671 0 : MJsonNode* tresult = MJsonNode::MakeArray();
672 0 : MJsonNode* lwresult = MJsonNode::MakeArray();
673 :
674 : HNDLE hDB;
675 0 : cm_get_experiment_database(&hDB, NULL);
676 :
677 0 : for (unsigned i=0; i<paths->size(); i++) {
678 0 : int status = 0;
679 : HNDLE hkey;
680 : KEY key;
681 0 : std::string path = (*paths)[i]->GetString();
682 :
683 0 : status = db_find_key(hDB, 0, path.c_str(), &hkey);
684 0 : if (status != DB_SUCCESS) {
685 0 : dresult->AddToArray(MJsonNode::MakeNull());
686 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
687 0 : tresult->AddToArray(MJsonNode::MakeNull());
688 0 : lwresult->AddToArray(MJsonNode::MakeNull());
689 0 : continue;
690 : }
691 :
692 0 : status = db_get_key(hDB, hkey, &key);
693 0 : if (status != DB_SUCCESS) {
694 0 : dresult->AddToArray(MJsonNode::MakeNull());
695 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
696 0 : tresult->AddToArray(MJsonNode::MakeNull());
697 0 : lwresult->AddToArray(MJsonNode::MakeNull());
698 0 : continue;
699 : }
700 :
701 0 : if (path.find("[") != std::string::npos) {
702 0 : std::vector<unsigned> list;
703 0 : status = parse_array_index_list("js_db_get_values", path.c_str(), &list);
704 :
705 0 : if (status != SUCCESS) {
706 0 : dresult->AddToArray(MJsonNode::MakeNull());
707 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
708 0 : tresult->AddToArray(MJsonNode::MakeInt(key.type));
709 0 : lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
710 0 : continue;
711 : }
712 :
713 0 : if (list.size() > 1) {
714 0 : MJsonNode *ddresult = MJsonNode::MakeArray();
715 0 : MJsonNode *ssresult = MJsonNode::MakeArray();
716 :
717 0 : for (unsigned i=0; i<list.size(); i++) {
718 0 : char* buf = NULL;
719 0 : int bufsize = 0;
720 0 : int end = 0;
721 :
722 0 : status = db_copy_json_index(hDB, hkey, list[i], &buf, &bufsize, &end);
723 0 : if (status == DB_SUCCESS) {
724 0 : ss_repair_utf8(buf);
725 0 : ddresult->AddToArray(MJsonNode::MakeJSON(buf));
726 0 : ssresult->AddToArray(MJsonNode::MakeInt(status));
727 : } else {
728 0 : ddresult->AddToArray(MJsonNode::MakeNull());
729 0 : ssresult->AddToArray(MJsonNode::MakeInt(status));
730 : }
731 :
732 0 : if (buf)
733 0 : free(buf);
734 : }
735 :
736 0 : dresult->AddToArray(ddresult);
737 0 : sresult->AddToArray(ssresult);
738 0 : tresult->AddToArray(MJsonNode::MakeInt(key.type));
739 0 : lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
740 :
741 : } else {
742 0 : char* buf = NULL;
743 0 : int bufsize = 0;
744 0 : int end = 0;
745 :
746 0 : status = db_copy_json_index(hDB, hkey, list[0], &buf, &bufsize, &end);
747 0 : if (status == DB_SUCCESS) {
748 0 : ss_repair_utf8(buf);
749 0 : dresult->AddToArray(MJsonNode::MakeJSON(buf));
750 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
751 0 : tresult->AddToArray(MJsonNode::MakeInt(key.type));
752 0 : lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
753 : } else {
754 0 : dresult->AddToArray(MJsonNode::MakeNull());
755 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
756 0 : tresult->AddToArray(MJsonNode::MakeInt(key.type));
757 0 : lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
758 : }
759 :
760 0 : if (buf)
761 0 : free(buf);
762 : }
763 0 : } else {
764 0 : char* buf = NULL;
765 0 : int bufsize = 0;
766 0 : int end = 0;
767 :
768 0 : status = db_copy_json_values(hDB, hkey, &buf, &bufsize, &end, omit_names,
769 : omit_last_written, omit_old_timestamp, preserve_case);
770 :
771 0 : if (status == DB_SUCCESS) {
772 0 : ss_repair_utf8(buf);
773 0 : dresult->AddToArray(MJsonNode::MakeJSON(buf));
774 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
775 0 : tresult->AddToArray(MJsonNode::MakeInt(key.type));
776 0 : lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
777 : } else {
778 0 : dresult->AddToArray(MJsonNode::MakeNull());
779 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
780 0 : tresult->AddToArray(MJsonNode::MakeInt(key.type));
781 0 : lwresult->AddToArray(MJsonNode::MakeInt(key.last_written));
782 : }
783 :
784 0 : if (buf)
785 0 : free(buf);
786 : }
787 0 : }
788 :
789 0 : MJsonNode* result = MJsonNode::MakeObject();
790 :
791 0 : result->AddToObject("data", dresult);
792 0 : result->AddToObject("status", sresult);
793 0 : if (!omit_tid)
794 0 : result->AddToObject("tid", tresult);
795 : else
796 0 : delete tresult;
797 0 : if (!omit_last_written)
798 0 : result->AddToObject("last_written", lwresult);
799 : else
800 0 : delete lwresult;
801 :
802 0 : return mjsonrpc_make_result(result);
803 : }
804 :
805 0 : static MJsonNode* js_db_ls(const MJsonNode* params)
806 : {
807 0 : if (!params) {
808 0 : MJSO* doc = MJSO::I();
809 0 : doc->D("get contents of given ODB subdirectory in the \"ls\" json encoding - similar to odbedit command \"ls -l\"");
810 0 : doc->P("paths[]", MJSON_STRING, "array of ODB subtree paths");
811 0 : doc->R("data[]", MJSON_OBJECT, "keys and values of ODB data for each path");
812 0 : doc->R("status[]", MJSON_INT, "return status of db_copy_json_ls() for each path");
813 0 : return doc;
814 : }
815 :
816 0 : MJsonNode* error = NULL;
817 :
818 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
819 :
820 0 : MJsonNode* dresult = MJsonNode::MakeArray();
821 0 : MJsonNode* sresult = MJsonNode::MakeArray();
822 :
823 : HNDLE hDB;
824 0 : cm_get_experiment_database(&hDB, NULL);
825 :
826 0 : for (unsigned i=0; i<paths->size(); i++) {
827 0 : int status = 0;
828 : HNDLE hkey;
829 0 : std::string path = (*paths)[i]->GetString();
830 :
831 0 : status = db_find_key(hDB, 0, path.c_str(), &hkey);
832 0 : if (status != DB_SUCCESS) {
833 0 : dresult->AddToArray(MJsonNode::MakeNull());
834 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
835 0 : continue;
836 : }
837 :
838 0 : char* buf = NULL;
839 0 : int bufsize = 0;
840 0 : int end = 0;
841 :
842 0 : status = db_copy_json_ls(hDB, hkey, &buf, &bufsize, &end);
843 :
844 0 : if (status == DB_SUCCESS) {
845 0 : ss_repair_utf8(buf);
846 0 : dresult->AddToArray(MJsonNode::MakeJSON(buf));
847 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
848 : } else {
849 0 : dresult->AddToArray(MJsonNode::MakeNull());
850 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
851 : }
852 :
853 0 : if (buf)
854 0 : free(buf);
855 0 : }
856 :
857 0 : return mjsonrpc_make_result("data", dresult, "status", sresult);
858 : }
859 :
860 0 : static MJsonNode* js_db_copy(const MJsonNode* params)
861 : {
862 0 : if (!params) {
863 0 : MJSO* doc = MJSO::I();
864 0 : doc->D("get complete ODB data in the \"save\" json encoding, suitable for reloading with odbedit command \"load\"");
865 0 : doc->P("paths[]", MJSON_STRING, "array of ODB subtree paths");
866 0 : doc->R("data[]", MJSON_OBJECT, "keys and values of ODB data for each path");
867 0 : doc->R("status[]", MJSON_INT, "return status of db_copy_json_save() for each path");
868 0 : return doc;
869 : }
870 :
871 0 : MJsonNode* error = NULL;
872 :
873 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
874 :
875 0 : MJsonNode* dresult = MJsonNode::MakeArray();
876 0 : MJsonNode* sresult = MJsonNode::MakeArray();
877 :
878 : HNDLE hDB;
879 0 : cm_get_experiment_database(&hDB, NULL);
880 :
881 0 : for (unsigned i=0; i<paths->size(); i++) {
882 0 : int status = 0;
883 : HNDLE hkey;
884 0 : std::string path = (*paths)[i]->GetString();
885 :
886 0 : status = db_find_key(hDB, 0, path.c_str(), &hkey);
887 0 : if (status != DB_SUCCESS) {
888 0 : dresult->AddToArray(MJsonNode::MakeNull());
889 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
890 0 : continue;
891 : }
892 :
893 0 : char* buf = NULL;
894 0 : int bufsize = 0;
895 0 : int end = 0;
896 :
897 0 : status = db_copy_json_save(hDB, hkey, &buf, &bufsize, &end);
898 :
899 0 : if (status == DB_SUCCESS) {
900 0 : ss_repair_utf8(buf);
901 0 : dresult->AddToArray(MJsonNode::MakeJSON(buf));
902 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
903 : } else {
904 0 : dresult->AddToArray(MJsonNode::MakeNull());
905 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
906 : }
907 :
908 0 : if (buf)
909 0 : free(buf);
910 0 : }
911 :
912 0 : return mjsonrpc_make_result("data", dresult, "status", sresult);
913 : }
914 :
915 0 : static MJsonNode* js_db_paste(const MJsonNode* params)
916 : {
917 0 : if (!params) {
918 0 : MJSO* doc = MJSO::I();
919 0 : doc->D("write data into ODB");
920 0 : doc->P("paths[]", MJSON_STRING, "array of ODB subtree paths, see note on array indices");
921 0 : doc->P("values[]", 0, "array of data values written to ODB via db_paste_json() for each path");
922 0 : doc->R("status[]", MJSON_INT, "array of return status of db_paste_json() for each path");
923 0 : return doc;
924 : }
925 :
926 0 : MJsonNode* error = NULL;
927 :
928 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
929 0 : const MJsonNodeVector* values = mjsonrpc_get_param_array(params, "values", &error); if (error) return error;
930 :
931 0 : if (paths->size() != values->size()) {
932 0 : return mjsonrpc_make_error(-32602, "Invalid params", "paths and values should have the same length");
933 : }
934 :
935 0 : MJsonNode* sresult = MJsonNode::MakeArray();
936 :
937 : HNDLE hDB;
938 0 : cm_get_experiment_database(&hDB, NULL);
939 :
940 0 : for (unsigned i=0; i<paths->size(); i++) {
941 0 : int status = 0;
942 : HNDLE hkey;
943 0 : std::string path = (*paths)[i]->GetString();
944 :
945 0 : status = db_find_key(hDB, 0, path.c_str(), &hkey);
946 0 : if (status != DB_SUCCESS) {
947 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
948 0 : continue;
949 : }
950 :
951 0 : const MJsonNode* v = (*values)[i];
952 0 : assert(v != NULL);
953 :
954 0 : if (path.find("[*]") != std::string::npos) {
955 :
956 : KEY key;
957 0 : db_get_key(hDB, hkey, &key);
958 0 : for (int j=0 ; j<key.num_values ; j++)
959 0 : status = db_paste_json_node(hDB, hkey, j, v);
960 :
961 0 : } else if (path.find("[") != std::string::npos) {
962 0 : std::vector<unsigned> list;
963 0 : status = parse_array_index_list("js_db_paste", path.c_str(), &list);
964 :
965 0 : if (status != SUCCESS) {
966 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
967 0 : continue;
968 : }
969 :
970 : // supported permutations of array indices and data values:
971 : // single index: intarray[1] -> data should be a single value: MJSON_ARRAY is rejected right here, MJSON_OBJECT is rejected by db_paste
972 : // multiple index intarray[1,2,3] -> data should be an array of equal length, or
973 : // multiple index intarray[1,2,3] -> if data is a single value, all array elements are set to this same value
974 :
975 0 : if (list.size() < 1) {
976 0 : cm_msg(MERROR, "js_db_paste", "invalid array indices for array path \"%s\"", path.c_str());
977 0 : sresult->AddToArray(MJsonNode::MakeInt(DB_TYPE_MISMATCH));
978 0 : continue;
979 0 : } else if (list.size() == 1) {
980 0 : if (v->GetType() == MJSON_ARRAY) {
981 0 : cm_msg(MERROR, "js_db_paste", "unexpected array of values for array path \"%s\"", path.c_str());
982 0 : sresult->AddToArray(MJsonNode::MakeInt(DB_TYPE_MISMATCH));
983 0 : continue;
984 : }
985 :
986 0 : status = db_paste_json_node(hDB, hkey, list[0], v);
987 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
988 0 : } else if ((list.size() > 1) && (v->GetType() == MJSON_ARRAY)) {
989 0 : const MJsonNodeVector* vvalues = v->GetArray();
990 :
991 0 : if (list.size() != vvalues->size()) {
992 0 : cm_msg(MERROR, "js_db_paste", "length of values array %d should be same as number of indices %d for array path \"%s\"", (int)vvalues->size(), (int)list.size(), path.c_str());
993 0 : sresult->AddToArray(MJsonNode::MakeInt(DB_TYPE_MISMATCH));
994 0 : continue;
995 : }
996 :
997 0 : MJsonNode *ssresult = MJsonNode::MakeArray();
998 :
999 0 : for (unsigned j =0; j <list.size(); j++) {
1000 0 : const MJsonNode* vv = (*vvalues)[j];
1001 :
1002 0 : if (vv == NULL) {
1003 0 : cm_msg(MERROR, "js_db_paste", "internal error: NULL array value at index %d for array path \"%s\"", j, path.c_str());
1004 0 : sresult->AddToArray(MJsonNode::MakeInt(DB_TYPE_MISMATCH));
1005 0 : continue;
1006 : }
1007 :
1008 0 : status = db_paste_json_node(hDB, hkey, list[j], vv);
1009 0 : ssresult->AddToArray(MJsonNode::MakeInt(status));
1010 : }
1011 :
1012 0 : sresult->AddToArray(ssresult);
1013 : } else {
1014 0 : MJsonNode *ssresult = MJsonNode::MakeArray();
1015 0 : for (unsigned j =0; j <list.size(); j++) {
1016 0 : status = db_paste_json_node(hDB, hkey, list[j], v);
1017 0 : ssresult->AddToArray(MJsonNode::MakeInt(status));
1018 : }
1019 0 : sresult->AddToArray(ssresult);
1020 : }
1021 0 : } else {
1022 0 : status = db_paste_json_node(hDB, hkey, 0, v);
1023 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1024 : }
1025 0 : }
1026 :
1027 0 : return mjsonrpc_make_result("status", sresult);
1028 : }
1029 :
1030 0 : static MJsonNode* js_db_create(const MJsonNode* params)
1031 : {
1032 0 : if (!params) {
1033 0 : MJSO* doc = MJSO::I();
1034 0 : doc->D("Create new ODB entries");
1035 0 : MJSO* o = doc->PA("array of ODB paths to be created")->AddObject("", "arguments to db_create_key() and db_set_num_values()");
1036 0 : o->Add("path", MJSON_STRING, "ODB path to be created");
1037 0 : o->Add("type", MJSON_INT, "MIDAS TID_xxx type");
1038 0 : o->Add("array_length?", MJSON_INT, "optional array length, default is 1");
1039 0 : o->Add("string_length?", MJSON_INT, "for TID_STRING, optional string length, default is NAME_LENGTH");
1040 0 : doc->R("status[]", MJSON_INT, "return status of db_create_key(), db_set_num_values() and db_set_data() (for TID_STRING) for each path");
1041 0 : return doc;
1042 : }
1043 :
1044 0 : MJsonNode* sresult = MJsonNode::MakeArray();
1045 :
1046 0 : const MJsonNodeVector* pp = params->GetArray();
1047 :
1048 0 : if (!pp) {
1049 0 : delete sresult;
1050 0 : return mjsonrpc_make_error(-32602, "Invalid params", "parameters must be an array of objects");
1051 : }
1052 :
1053 : HNDLE hDB;
1054 0 : cm_get_experiment_database(&hDB, NULL);
1055 :
1056 0 : for (unsigned i=0; i<pp->size(); i++) {
1057 0 : const MJsonNode* p = (*pp)[i];
1058 0 : std::string path = mjsonrpc_get_param(p, "path", NULL)->GetString();
1059 0 : int type = mjsonrpc_get_param(p, "type", NULL)->GetInt();
1060 0 : int array_length = mjsonrpc_get_param(p, "array_length", NULL)->GetInt();
1061 0 : int string_length = mjsonrpc_get_param(p, "string_length", NULL)->GetInt();
1062 :
1063 : //printf("create odb [%s], type %d, array %d, string %d\n", path.c_str(), type, array_length, string_length);
1064 :
1065 0 : if (string_length == 0)
1066 0 : string_length = NAME_LENGTH;
1067 :
1068 0 : int status = db_create_key(hDB, 0, path.c_str(), type);
1069 :
1070 0 : if (status == DB_SUCCESS && string_length > 0 && type == TID_STRING) {
1071 : HNDLE hKey;
1072 0 : status = db_find_key(hDB, 0, path.c_str(), &hKey);
1073 0 : if (status == DB_SUCCESS) {
1074 0 : char* buf = (char*)calloc(1, string_length);
1075 0 : assert(buf != NULL);
1076 0 : int size = string_length;
1077 0 : status = db_set_data(hDB, hKey, buf, size, 1, TID_STRING);
1078 0 : free(buf);
1079 : }
1080 : }
1081 :
1082 0 : if (status == DB_SUCCESS && array_length > 1) {
1083 : HNDLE hKey;
1084 0 : status = db_find_key(hDB, 0, path.c_str(), &hKey);
1085 0 : if (status == DB_SUCCESS)
1086 0 : status = db_set_num_values(hDB, hKey, array_length);
1087 : }
1088 :
1089 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1090 0 : }
1091 :
1092 0 : return mjsonrpc_make_result("status", sresult);
1093 : }
1094 :
1095 0 : static MJsonNode* js_db_delete(const MJsonNode* params)
1096 : {
1097 0 : if (!params) {
1098 0 : MJSO* doc = MJSO::I();
1099 0 : doc->D("delete ODB keys");
1100 0 : doc->P("paths[]", MJSON_STRING, "array of ODB paths to delete");
1101 0 : doc->R("status[]", MJSON_INT, "return status of db_delete_key() for each path");
1102 0 : return doc;
1103 : }
1104 :
1105 0 : MJsonNode* error = NULL;
1106 :
1107 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
1108 :
1109 0 : MJsonNode* sresult = MJsonNode::MakeArray();
1110 :
1111 : HNDLE hDB;
1112 0 : cm_get_experiment_database(&hDB, NULL);
1113 :
1114 0 : for (unsigned i=0; i<paths->size(); i++) {
1115 0 : int status = 0;
1116 : HNDLE hkey;
1117 0 : std::string path = (*paths)[i]->GetString();
1118 :
1119 0 : status = db_find_link(hDB, 0, path.c_str(), &hkey);
1120 0 : if (status != DB_SUCCESS) {
1121 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1122 0 : continue;
1123 : }
1124 :
1125 0 : status = db_delete_key(hDB, hkey, false);
1126 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1127 0 : }
1128 :
1129 0 : return mjsonrpc_make_result("status", sresult);
1130 : }
1131 :
1132 0 : static MJsonNode* js_db_resize(const MJsonNode* params)
1133 : {
1134 0 : if (!params) {
1135 0 : MJSO* doc = MJSO::I();
1136 0 : doc->D("Change size of ODB arrays");
1137 0 : doc->P("paths[]", MJSON_STRING, "array of ODB paths to resize");
1138 0 : doc->P("new_lengths[]", MJSON_INT, "array of new lengths for each ODB path");
1139 0 : doc->R("status[]", MJSON_INT, "return status of db_set_num_values() for each path");
1140 0 : return doc;
1141 : }
1142 :
1143 0 : MJsonNode* error = NULL;
1144 :
1145 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
1146 0 : const MJsonNodeVector* lengths = mjsonrpc_get_param_array(params, "new_lengths", &error); if (error) return error;
1147 :
1148 0 : if (paths->size() != lengths->size()) {
1149 0 : return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"new_lengths\" should have the same length");
1150 : }
1151 :
1152 0 : MJsonNode* sresult = MJsonNode::MakeArray();
1153 :
1154 : HNDLE hDB;
1155 0 : cm_get_experiment_database(&hDB, NULL);
1156 :
1157 0 : for (unsigned i=0; i<paths->size(); i++) {
1158 0 : int status = 0;
1159 : HNDLE hkey;
1160 0 : std::string path = (*paths)[i]->GetString();
1161 :
1162 0 : status = db_find_key(hDB, 0, path.c_str(), &hkey);
1163 0 : if (status != DB_SUCCESS) {
1164 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1165 0 : continue;
1166 : }
1167 :
1168 0 : int length = (*lengths)[i]->GetInt();
1169 0 : if (length < 1) {
1170 0 : sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
1171 0 : continue;
1172 : }
1173 :
1174 0 : status = db_set_num_values(hDB, hkey, length);
1175 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1176 0 : }
1177 :
1178 0 : return mjsonrpc_make_result("status", sresult);
1179 : }
1180 :
1181 0 : static MJsonNode* js_db_resize_string(const MJsonNode* params)
1182 : {
1183 0 : if (!params) {
1184 0 : MJSO* doc = MJSO::I();
1185 0 : doc->D("Change size of ODB string arrays");
1186 0 : doc->P("paths[]", MJSON_STRING, "array of ODB paths to resize");
1187 0 : doc->P("new_lengths[]", MJSON_INT, "array of new lengths for each ODB path");
1188 0 : doc->P("new_string_lengths[]", MJSON_INT, "array of new string lengths for each ODB path");
1189 0 : doc->R("status[]", MJSON_INT, "return status of db_resize_string() for each path");
1190 0 : return doc;
1191 : }
1192 :
1193 0 : MJsonNode* error = NULL;
1194 :
1195 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
1196 0 : const MJsonNodeVector* lengths = mjsonrpc_get_param_array(params, "new_lengths", &error); if (error) return error;
1197 0 : const MJsonNodeVector* string_lengths = mjsonrpc_get_param_array(params, "new_string_lengths", &error); if (error) return error;
1198 :
1199 0 : if (paths->size() != lengths->size()) {
1200 0 : return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"new_lengths\" should have the same length");
1201 : }
1202 :
1203 0 : if (paths->size() != string_lengths->size()) {
1204 0 : return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"new_string_lengths\" should have the same length");
1205 : }
1206 :
1207 0 : MJsonNode* sresult = MJsonNode::MakeArray();
1208 :
1209 : HNDLE hDB;
1210 0 : cm_get_experiment_database(&hDB, NULL);
1211 :
1212 0 : for (unsigned i=0; i<paths->size(); i++) {
1213 0 : std::string path = (*paths)[i]->GetString();
1214 :
1215 0 : int length = (*lengths)[i]->GetInt();
1216 0 : if (length < 0) {
1217 0 : sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
1218 0 : continue;
1219 : }
1220 :
1221 0 : int string_length = (*string_lengths)[i]->GetInt();
1222 0 : if (length < 0) {
1223 0 : sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
1224 0 : continue;
1225 : }
1226 :
1227 0 : int status = db_resize_string(hDB, 0, path.c_str(), length, string_length);
1228 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1229 0 : }
1230 :
1231 0 : return mjsonrpc_make_result("status", sresult);
1232 : }
1233 :
1234 0 : static MJsonNode* js_db_key(const MJsonNode* params)
1235 : {
1236 0 : if (!params) {
1237 0 : MJSO* doc = MJSO::I();
1238 0 : doc->D("get ODB keys");
1239 0 : doc->P("paths[]", MJSON_STRING, "array of ODB paths");
1240 0 : doc->R("status[]", MJSON_INT, "return status of db_key() for each path");
1241 0 : doc->R("keys[]", MJSON_OBJECT, "key data for each path");
1242 0 : doc->R("keys[].type", MJSON_INT, "key type TID_xxx");
1243 0 : doc->R("keys[].num_values", MJSON_INT, "array length, 1 for normal entries");
1244 0 : doc->R("keys[].name", MJSON_STRING, "key name");
1245 0 : doc->R("keys[].total_size", MJSON_INT, "data total size in bytes");
1246 0 : doc->R("keys[].item_size", MJSON_INT, "array element size, string length for TID_STRING");
1247 0 : doc->R("keys[].access_mode", MJSON_INT, "access mode bitmap of MODE_xxx");
1248 0 : doc->R("keys[].notify_count", MJSON_INT, "number of hotlinks attached to this key");
1249 0 : doc->R("keys[].last_written", MJSON_INT, "timestamp when data was last updated");
1250 0 : return doc;
1251 : }
1252 :
1253 0 : MJsonNode* error = NULL;
1254 :
1255 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
1256 :
1257 0 : MJsonNode* kresult = MJsonNode::MakeArray();
1258 0 : MJsonNode* sresult = MJsonNode::MakeArray();
1259 :
1260 : HNDLE hDB;
1261 0 : cm_get_experiment_database(&hDB, NULL);
1262 :
1263 0 : for (unsigned i=0; i<paths->size(); i++) {
1264 0 : int status = 0;
1265 : HNDLE hkey;
1266 : KEY key;
1267 0 : std::string path = (*paths)[i]->GetString();
1268 :
1269 0 : status = db_find_key(hDB, 0, path.c_str(), &hkey);
1270 0 : if (status != DB_SUCCESS) {
1271 0 : kresult->AddToArray(MJsonNode::MakeNull());
1272 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1273 0 : continue;
1274 : }
1275 :
1276 0 : status = db_get_key(hDB, hkey, &key);
1277 0 : if (status != DB_SUCCESS) {
1278 0 : kresult->AddToArray(MJsonNode::MakeNull());
1279 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1280 0 : continue;
1281 : }
1282 :
1283 0 : MJsonNode* jkey = MJsonNode::MakeObject();
1284 :
1285 0 : jkey->AddToObject("type", MJsonNode::MakeInt(key.type));
1286 0 : jkey->AddToObject("num_values", MJsonNode::MakeInt(key.num_values));
1287 0 : ss_repair_utf8(key.name);
1288 0 : jkey->AddToObject("name", MJsonNode::MakeString(key.name));
1289 0 : jkey->AddToObject("total_size", MJsonNode::MakeInt(key.total_size));
1290 0 : jkey->AddToObject("item_size", MJsonNode::MakeInt(key.item_size));
1291 0 : jkey->AddToObject("access_mode", MJsonNode::MakeInt(key.access_mode));
1292 0 : jkey->AddToObject("notify_count", MJsonNode::MakeInt(key.notify_count));
1293 0 : jkey->AddToObject("last_written", MJsonNode::MakeInt(key.last_written));
1294 :
1295 0 : kresult->AddToArray(jkey);
1296 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1297 0 : }
1298 :
1299 0 : return mjsonrpc_make_result("keys", kresult, "status", sresult);
1300 : }
1301 :
1302 0 : static MJsonNode* js_db_rename(const MJsonNode* params)
1303 : {
1304 0 : if (!params) {
1305 0 : MJSO* doc = MJSO::I();
1306 0 : doc->D("Change size of ODB arrays");
1307 0 : doc->P("paths[]", MJSON_STRING, "array of ODB paths to rename");
1308 0 : doc->P("new_names[]", MJSON_STRING, "array of new names for each ODB path");
1309 0 : doc->R("status[]", MJSON_INT, "return status of db_rename_key() for each path");
1310 0 : return doc;
1311 : }
1312 :
1313 0 : MJsonNode* error = NULL;
1314 :
1315 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
1316 0 : const MJsonNodeVector* names = mjsonrpc_get_param_array(params, "new_names", &error); if (error) return error;
1317 :
1318 0 : if (paths->size() != names->size()) {
1319 0 : return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"new_names\" should have the same length");
1320 : }
1321 :
1322 0 : MJsonNode* sresult = MJsonNode::MakeArray();
1323 :
1324 : HNDLE hDB;
1325 0 : cm_get_experiment_database(&hDB, NULL);
1326 :
1327 0 : for (unsigned i=0; i<paths->size(); i++) {
1328 0 : int status = 0;
1329 : HNDLE hkey;
1330 0 : std::string path = (*paths)[i]->GetString();
1331 :
1332 0 : status = db_find_link(hDB, 0, path.c_str(), &hkey);
1333 0 : if (status != DB_SUCCESS) {
1334 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1335 0 : continue;
1336 : }
1337 :
1338 0 : std::string new_name = (*names)[i]->GetString();
1339 0 : if (new_name.length() < 1) {
1340 0 : sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
1341 0 : continue;
1342 : }
1343 :
1344 0 : status = db_rename_key(hDB, hkey, new_name.c_str());
1345 :
1346 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1347 0 : }
1348 :
1349 0 : return mjsonrpc_make_result("status", sresult);
1350 : }
1351 :
1352 0 : static MJsonNode* js_db_link(const MJsonNode* params)
1353 : {
1354 0 : if (!params) {
1355 0 : MJSO* doc = MJSO::I();
1356 0 : doc->D("Create ODB symlinks");
1357 0 : doc->P("new_links[]", MJSON_STRING, "array of new symlinks to be created");
1358 0 : doc->P("target_paths[]", MJSON_STRING, "array of existing ODB paths for each link");
1359 0 : doc->R("status[]", MJSON_INT, "return status of db_create_link() for each path");
1360 0 : return doc;
1361 : }
1362 :
1363 0 : MJsonNode* error = NULL;
1364 :
1365 0 : const MJsonNodeVector* target_paths = mjsonrpc_get_param_array(params, "target_paths", &error); if (error) return error;
1366 0 : const MJsonNodeVector* new_links = mjsonrpc_get_param_array(params, "new_links", &error); if (error) return error;
1367 :
1368 0 : if (target_paths->size() != new_links->size()) {
1369 0 : return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"target_paths\" and \"new_links\" should have the same length");
1370 : }
1371 :
1372 0 : MJsonNode* sresult = MJsonNode::MakeArray();
1373 :
1374 : HNDLE hDB;
1375 0 : cm_get_experiment_database(&hDB, NULL);
1376 :
1377 0 : for (unsigned i=0; i<new_links->size(); i++) {
1378 0 : int status = 0;
1379 0 : std::string target_path = (*target_paths)[i]->GetString();
1380 0 : std::string new_link = (*new_links)[i]->GetString();
1381 0 : if (new_link.length() < 1) {
1382 0 : sresult->AddToArray(MJsonNode::MakeInt(DB_INVALID_PARAM));
1383 0 : continue;
1384 : }
1385 :
1386 0 : status = db_create_link(hDB, 0, new_link.c_str(), target_path.c_str());
1387 :
1388 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1389 0 : }
1390 :
1391 0 : return mjsonrpc_make_result("status", sresult);
1392 : }
1393 :
1394 0 : static MJsonNode* js_db_reorder(const MJsonNode* params)
1395 : {
1396 0 : if (!params) {
1397 0 : MJSO* doc = MJSO::I();
1398 0 : doc->D("Change order of ODB keys in a subdirectory");
1399 0 : doc->P("paths[]", MJSON_STRING, "array of new symlinks to be created");
1400 0 : doc->P("indices[]", MJSON_INT, "array of existing ODB paths for each link");
1401 0 : doc->R("status[]", MJSON_INT, "return status of db_reorder_key() for each path");
1402 0 : return doc;
1403 : }
1404 :
1405 0 : MJsonNode* error = NULL;
1406 :
1407 0 : const MJsonNodeVector* paths = mjsonrpc_get_param_array(params, "paths", &error); if (error) return error;
1408 0 : const MJsonNodeVector* indices = mjsonrpc_get_param_array(params, "indices", &error); if (error) return error;
1409 :
1410 0 : if (paths->size() != indices->size()) {
1411 0 : return mjsonrpc_make_error(-32602, "Invalid params", "arrays \"paths\" and \"indices\" should have the same length");
1412 : }
1413 :
1414 0 : MJsonNode* sresult = MJsonNode::MakeArray();
1415 :
1416 : HNDLE hDB;
1417 0 : cm_get_experiment_database(&hDB, NULL);
1418 :
1419 0 : for (unsigned i=0; i<paths->size(); i++) {
1420 0 : int status = 0;
1421 : HNDLE hkey;
1422 0 : std::string path = (*paths)[i]->GetString();
1423 0 : int index = (*indices)[i]->GetInt();
1424 :
1425 0 : status = db_find_key(hDB, 0, path.c_str(), &hkey);
1426 0 : if (status != DB_SUCCESS) {
1427 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1428 0 : continue;
1429 : }
1430 :
1431 0 : status = db_reorder_key(hDB, hkey, index);
1432 :
1433 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1434 0 : }
1435 :
1436 0 : return mjsonrpc_make_result("status", sresult);
1437 : }
1438 :
1439 0 : static MJsonNode* js_db_sor(const MJsonNode* params)
1440 : {
1441 0 : if (!params) {
1442 0 : MJSO* doc = MJSO::I();
1443 0 : doc->D("Show ODB open records starting from given ODB path");
1444 0 : doc->P("path?", MJSON_STRING, "ODB path");
1445 0 : doc->R("sor", MJSON_JSON, "return value of db_sor()");
1446 0 : return doc;
1447 : }
1448 :
1449 0 : MJsonNode* error = NULL;
1450 :
1451 0 : std::string path = mjsonrpc_get_param(params, "path", NULL)->GetString(); if (error) return error;
1452 :
1453 : HNDLE hDB;
1454 0 : cm_get_experiment_database(&hDB, NULL);
1455 :
1456 0 : MJsonNode* sor = db_sor(hDB, path.c_str());
1457 :
1458 0 : return mjsonrpc_make_result("sor", sor);
1459 0 : }
1460 :
1461 0 : static MJsonNode* js_db_scl(const MJsonNode* params)
1462 : {
1463 0 : if (!params) {
1464 0 : MJSO* doc = MJSO::I();
1465 0 : doc->D("Show ODB clients");
1466 0 : doc->R("scl", MJSON_JSON, "return value of db_scl()");
1467 0 : return doc;
1468 : }
1469 :
1470 : HNDLE hDB;
1471 0 : cm_get_experiment_database(&hDB, NULL);
1472 :
1473 0 : MJsonNode* scl = db_scl(hDB);
1474 :
1475 0 : return mjsonrpc_make_result("scl", scl);
1476 : }
1477 :
1478 : /////////////////////////////////////////////////////////////////////////////////
1479 : //
1480 : // cm_msg code goes here
1481 : //
1482 : /////////////////////////////////////////////////////////////////////////////////
1483 :
1484 0 : static MJsonNode* js_cm_msg_facilities(const MJsonNode* params)
1485 : {
1486 0 : if (!params) {
1487 0 : MJSO* doc = MJSO::I();
1488 0 : doc->D("get message facilities using cm_msg_facilities()");
1489 0 : doc->R("status", MJSON_INT, "return status of cm_msg_facilities()");
1490 0 : doc->R("facilities[]", MJSON_STRING, "array of facility names");
1491 0 : return doc;
1492 : }
1493 :
1494 0 : STRING_LIST list;
1495 :
1496 0 : int status = cm_msg_facilities(&list);
1497 :
1498 0 : MJsonNode* facilities = MJsonNode::MakeArray();
1499 :
1500 0 : for (unsigned i=0; i<list.size(); i++) {
1501 0 : ss_repair_utf8(list[i]);
1502 0 : facilities->AddToArray(MJsonNode::MakeString(list[i].c_str()));
1503 : }
1504 :
1505 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status),
1506 0 : "facilities", facilities);
1507 0 : }
1508 :
1509 0 : static MJsonNode* js_cm_msg1(const MJsonNode* params)
1510 : {
1511 0 : if (!params) {
1512 0 : MJSO *doc = MJSO::I();
1513 0 : doc->D("Generate a midas message using cm_msg1()");
1514 0 : doc->P("facility?", MJSON_STRING, "message facility, default is \"midas\"");
1515 0 : doc->P("user?", MJSON_STRING, "message user, default is \"javascript_commands\"");
1516 0 : doc->P("type?", MJSON_INT, "message type, MT_xxx from midas.h, default is MT_INFO");
1517 0 : doc->P("message", MJSON_STRING, "message text");
1518 0 : doc->R("status", MJSON_INT, "return status of cm_msg1()");
1519 0 : return doc;
1520 : }
1521 :
1522 0 : MJsonNode* error = NULL;
1523 :
1524 0 : std::string facility = mjsonrpc_get_param(params, "facility", NULL)->GetString();
1525 0 : std::string user = mjsonrpc_get_param(params, "user", NULL)->GetString();
1526 0 : int type = mjsonrpc_get_param(params, "type", NULL)->GetInt();
1527 0 : std::string message = mjsonrpc_get_param(params, "message", &error)->GetString(); if (error) return error;
1528 :
1529 0 : if (facility.size() <1)
1530 0 : facility = "midas";
1531 0 : if (user.size()<1)
1532 0 : user = "javascript_commands";
1533 0 : if (type == 0)
1534 0 : type = MT_INFO;
1535 :
1536 0 : int status = cm_msg1(type, __FILE__, __LINE__, facility.c_str(), user.c_str(), "%s", message.c_str());
1537 :
1538 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
1539 0 : }
1540 :
1541 0 : static MJsonNode* js_cm_msg_retrieve(const MJsonNode* params)
1542 : {
1543 0 : if (!params) {
1544 0 : MJSO *doc = MJSO::I();
1545 0 : doc->D("Retrieve midas messages using cm_msg_retrieve2()");
1546 0 : doc->P("facility?", MJSON_STRING, "message facility, default is \"midas\"");
1547 0 : doc->P("min_messages?", MJSON_INT, "get at least this many messages, default is 1");
1548 0 : doc->P("time?", MJSON_NUMBER, "start from given timestamp, value 0 means give me newest messages, default is 0");
1549 0 : doc->R("num_messages", MJSON_INT, "number of messages returned");
1550 0 : doc->R("messages", MJSON_STRING, "messages separated by \\n");
1551 0 : doc->R("status", MJSON_INT, "return status of cm_msg_retrieve2()");
1552 0 : return doc;
1553 : }
1554 :
1555 0 : std::string facility = mjsonrpc_get_param(params, "facility", NULL)->GetString();
1556 0 : int min_messages = mjsonrpc_get_param(params, "min_messages", NULL)->GetInt();
1557 0 : double time = mjsonrpc_get_param(params, "time", NULL)->GetDouble();
1558 :
1559 0 : if (facility.size() < 1)
1560 0 : facility = "midas";
1561 :
1562 0 : int num_messages = 0;
1563 0 : char* messages = NULL;
1564 :
1565 0 : int status = cm_msg_retrieve2(facility.c_str(), (time_t)time, min_messages, &messages, &num_messages);
1566 :
1567 0 : MJsonNode* result = MJsonNode::MakeObject();
1568 :
1569 0 : result->AddToObject("status", MJsonNode::MakeInt(status));
1570 0 : result->AddToObject("num_messages", MJsonNode::MakeInt(num_messages));
1571 :
1572 0 : if (messages) {
1573 0 : ss_repair_utf8(messages);
1574 0 : result->AddToObject("messages", MJsonNode::MakeString(messages));
1575 0 : free(messages);
1576 0 : messages = NULL;
1577 : }
1578 :
1579 0 : return mjsonrpc_make_result(result);
1580 0 : }
1581 :
1582 : /////////////////////////////////////////////////////////////////////////////////
1583 : //
1584 : // Alarm code goes here
1585 : //
1586 : /////////////////////////////////////////////////////////////////////////////////
1587 :
1588 0 : static MJsonNode* js_al_reset_alarm(const MJsonNode* params)
1589 : {
1590 0 : if (!params) {
1591 0 : MJSO* doc = MJSO::I();
1592 0 : doc->D("reset alarms");
1593 0 : doc->P("alarms[]", MJSON_STRING, "array of alarm names");
1594 0 : doc->R("status[]", MJSON_INT, "return status of al_reset_alarm() for each alarm");
1595 0 : return doc;
1596 : }
1597 :
1598 0 : MJsonNode* error = NULL;
1599 :
1600 0 : const MJsonNodeVector* alarms = mjsonrpc_get_param_array(params, "alarms", &error); if (error) return error;
1601 :
1602 0 : MJsonNode* sresult = MJsonNode::MakeArray();
1603 :
1604 0 : for (unsigned i=0; i<alarms->size(); i++) {
1605 0 : int status = al_reset_alarm((*alarms)[i]->GetString().c_str());
1606 0 : sresult->AddToArray(MJsonNode::MakeInt(status));
1607 : }
1608 :
1609 0 : return mjsonrpc_make_result("status", sresult);
1610 : }
1611 :
1612 0 : static MJsonNode* js_al_trigger_alarm(const MJsonNode* params)
1613 : {
1614 0 : if (!params) {
1615 0 : MJSO* doc = MJSO::I();
1616 0 : doc->D("trigger an alarm");
1617 0 : doc->P("name", MJSON_STRING, "alarm name");
1618 0 : doc->P("message", MJSON_STRING, "alarm message");
1619 0 : doc->P("class", MJSON_STRING, "alarm class");
1620 0 : doc->P("condition", MJSON_STRING, "alarm condition");
1621 0 : doc->P("type", MJSON_INT, "alarm type (AT_xxx)");
1622 0 : doc->R("status", MJSON_INT, "return status of al_trigger_alarm()");
1623 0 : return doc;
1624 : }
1625 :
1626 0 : MJsonNode* error = NULL;
1627 :
1628 0 : std::string name = mjsonrpc_get_param(params, "name", &error)->GetString(); if (error) return error;
1629 0 : std::string message = mjsonrpc_get_param(params, "message", &error)->GetString(); if (error) return error;
1630 0 : std::string xclass = mjsonrpc_get_param(params, "class", &error)->GetString(); if (error) return error;
1631 0 : std::string condition = mjsonrpc_get_param(params, "condition", &error)->GetString(); if (error) return error;
1632 0 : int type = mjsonrpc_get_param(params, "type", &error)->GetInt(); if (error) return error;
1633 :
1634 0 : int status = al_trigger_alarm(name.c_str(), message.c_str(), xclass.c_str(), condition.c_str(), type);
1635 :
1636 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
1637 0 : }
1638 :
1639 0 : static MJsonNode* js_al_trigger_class(const MJsonNode* params)
1640 : {
1641 0 : if (!params) {
1642 0 : MJSO* doc = MJSO::I();
1643 0 : doc->D("trigger an alarm");
1644 0 : doc->P("class", MJSON_STRING, "alarm class");
1645 0 : doc->P("message", MJSON_STRING, "alarm message");
1646 0 : doc->P("first?", MJSON_BOOL, "see al_trigger_class() in midas.c");
1647 0 : doc->R("status", MJSON_INT, "return status of al_trigger_class()");
1648 0 : return doc;
1649 : }
1650 :
1651 0 : MJsonNode* error = NULL;
1652 :
1653 0 : std::string xclass = mjsonrpc_get_param(params, "class", &error)->GetString(); if (error) return error;
1654 0 : std::string message = mjsonrpc_get_param(params, "message", &error)->GetString(); if (error) return error;
1655 0 : bool first = mjsonrpc_get_param(params, "first", NULL)->GetBool();
1656 :
1657 0 : int status = al_trigger_class(xclass.c_str(), message.c_str(), first);
1658 :
1659 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
1660 0 : }
1661 :
1662 : /////////////////////////////////////////////////////////////////////////////////
1663 : //
1664 : // History code goes here
1665 : //
1666 : /////////////////////////////////////////////////////////////////////////////////
1667 :
1668 : #include "history.h"
1669 :
1670 0 : static MJsonNode* js_hs_get_active_events(const MJsonNode* params)
1671 : {
1672 0 : if (!params) {
1673 0 : MJSO* doc = MJSO::I();
1674 0 : doc->D("get list of active history events using hs_read_event_list()");
1675 0 : doc->R("status", MJSON_INT, "return status of hs_read_event_list()");
1676 0 : doc->R("events[]", MJSON_STRING, "array of history event names");
1677 0 : return doc;
1678 : }
1679 :
1680 0 : STRING_LIST list;
1681 :
1682 0 : int status = hs_read_event_list(&list);
1683 :
1684 0 : MJsonNode* events = MJsonNode::MakeArray();
1685 :
1686 0 : for (unsigned i=0; i<list.size(); i++) {
1687 0 : ss_repair_utf8(list[i]);
1688 0 : events->AddToArray(MJsonNode::MakeString(list[i].c_str()));
1689 : }
1690 :
1691 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "events", events);
1692 0 : }
1693 :
1694 : typedef std::map<std::string,MidasHistoryInterface*> MhiMap;
1695 :
1696 : static MhiMap gHistoryChannels;
1697 :
1698 0 : static MidasHistoryInterface* GetHistory(const char* name)
1699 : {
1700 : // empty name means use the default reader channel
1701 :
1702 0 : MhiMap::iterator ci = gHistoryChannels.find(name);
1703 0 : if (ci != gHistoryChannels.end()) {
1704 0 : return ci->second;
1705 : };
1706 :
1707 0 : int verbose = 0;
1708 :
1709 : HNDLE hDB;
1710 0 : cm_get_experiment_database(&hDB, NULL);
1711 :
1712 0 : HNDLE hKey = 0;
1713 :
1714 0 : if (strlen(name) < 1) {
1715 0 : int status = hs_find_reader_channel(hDB, &hKey, verbose);
1716 0 : if (status != HS_SUCCESS) {
1717 0 : return NULL;
1718 : }
1719 : } else {
1720 : HNDLE hKeyChan;
1721 0 : int status = db_find_key(hDB, 0, "/Logger/History", &hKeyChan);
1722 0 : if (status != DB_SUCCESS) {
1723 0 : return NULL;
1724 : }
1725 0 : status = db_find_key(hDB, hKeyChan, name, &hKey);
1726 0 : if (status != DB_SUCCESS) {
1727 0 : return NULL;
1728 : }
1729 : }
1730 :
1731 0 : MidasHistoryInterface* mh = NULL;
1732 :
1733 0 : int status = hs_get_history(hDB, hKey, HS_GET_READER|HS_GET_INACTIVE, verbose, &mh);
1734 0 : if (status != HS_SUCCESS || mh==NULL) {
1735 0 : cm_msg(MERROR, "GetHistory", "Cannot configure history, hs_get_history() status %d", status);
1736 0 : return NULL;
1737 : }
1738 :
1739 : //printf("hs_get_history: \"%s\" -> mh %p\n", name, mh);
1740 :
1741 0 : gHistoryChannels[name] = mh;
1742 :
1743 : // cm_msg(MINFO, "GetHistory", "Reading history channel \"%s\" from channel \'%s\' type \'%s\'", name, mh->name, mh->type);
1744 :
1745 0 : return mh;
1746 : }
1747 :
1748 0 : static void js_hs_exit()
1749 : {
1750 0 : for (auto& e : gHistoryChannels) {
1751 : //printf("history channel \"%s\" mh %p\n", e.first.c_str(), e.second);
1752 0 : delete e.second;
1753 : }
1754 0 : gHistoryChannels.clear();
1755 0 : }
1756 :
1757 0 : static MJsonNode* js_hs_get_channels(const MJsonNode* params)
1758 : {
1759 0 : if (!params) {
1760 0 : MJSO* doc = MJSO::I();
1761 0 : doc->D("get list of history channels in /Logger/History");
1762 0 : doc->R("status", MJSON_INT, "return success or failure status");
1763 0 : doc->R("default_channel", MJSON_STRING, "name of the default logger history channel");
1764 0 : doc->R("channels[]", MJSON_STRING, "all logger history channel names");
1765 0 : doc->R("active_channels[]", MJSON_STRING, "active logger history channel names");
1766 0 : return doc;
1767 : }
1768 :
1769 0 : MJsonNode* channels = MJsonNode::MakeArray();
1770 0 : MJsonNode* active_channels = MJsonNode::MakeArray();
1771 :
1772 : HNDLE hDB;
1773 0 : cm_get_experiment_database(&hDB, NULL);
1774 :
1775 : // get history channel name selected by user in ODB
1776 :
1777 : //std::string selected_channel;
1778 : //db_get_value_string(hDB, 0, "/History/LoggerHistoryChannel", 0, &selected_channel, TRUE);
1779 :
1780 : int status;
1781 : HNDLE hKeyChan;
1782 :
1783 0 : status = db_find_key(hDB, 0, "/Logger/History", &hKeyChan);
1784 0 : if (status == DB_SUCCESS) {
1785 0 : for (int ichan=0; ; ichan++) {
1786 : HNDLE hKey;
1787 0 : status = db_enum_key(hDB, hKeyChan, ichan, &hKey);
1788 0 : if (status == DB_NO_MORE_SUBKEYS) {
1789 0 : status = DB_SUCCESS;
1790 0 : break;
1791 : }
1792 0 : if (status != DB_SUCCESS)
1793 0 : break;
1794 :
1795 : KEY key;
1796 :
1797 0 : status = db_get_key(hDB, hKey, &key);
1798 :
1799 0 : if (status == DB_SUCCESS) {
1800 0 : ss_repair_utf8(key.name);
1801 0 : channels->AddToArray(MJsonNode::MakeString(key.name));
1802 :
1803 0 : INT active = 0;
1804 0 : INT size = sizeof(active);
1805 0 : status = db_get_value(hDB, hKey, "Active", &active, &size, TID_BOOL, FALSE);
1806 0 : if (status == DB_SUCCESS) {
1807 0 : if (active) {
1808 0 : active_channels->AddToArray(MJsonNode::MakeString(key.name));
1809 : }
1810 : }
1811 : }
1812 0 : }
1813 : }
1814 :
1815 0 : std::string default_channel;
1816 :
1817 : HNDLE hKey;
1818 0 : status = hs_find_reader_channel(hDB, &hKey, 0);
1819 0 : if (status == DB_SUCCESS) {
1820 : KEY key;
1821 0 : status = db_get_key(hDB, hKey, &key);
1822 0 : if (status == DB_SUCCESS) {
1823 0 : default_channel = key.name;
1824 : }
1825 : }
1826 :
1827 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(1),
1828 : //"selected_channel", MJsonNode::MakeString(selected_channel.c_str()),
1829 : "default_channel", MJsonNode::MakeString(default_channel.c_str()),
1830 : "active_channels", active_channels,
1831 0 : "channels", channels);
1832 0 : }
1833 :
1834 0 : static MJsonNode* js_hs_get_events(const MJsonNode* params)
1835 : {
1836 0 : if (!params) {
1837 0 : MJSO* doc = MJSO::I();
1838 0 : doc->D("get list of history events that existed at give time using hs_get_events()");
1839 0 : doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
1840 0 : doc->P("time?", MJSON_NUMBER, "timestamp, value 0 means current time, default is 0");
1841 0 : doc->R("status", MJSON_INT, "return status of hs_get_events()");
1842 0 : doc->R("channel", MJSON_STRING, "logger history channel name");
1843 0 : doc->R("events[]", MJSON_STRING, "array of history event names");
1844 0 : return doc;
1845 : }
1846 :
1847 0 : std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
1848 0 : double time = mjsonrpc_get_param(params, "time", NULL)->GetDouble();
1849 :
1850 0 : MidasHistoryInterface* mh = GetHistory(channel.c_str());
1851 :
1852 0 : MJsonNode* events = MJsonNode::MakeArray();
1853 :
1854 0 : if (!mh) {
1855 0 : int status = HS_FILE_ERROR;
1856 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "events", events);
1857 : }
1858 :
1859 0 : if (time == 0) {
1860 0 : time = ::time(NULL);
1861 : }
1862 :
1863 0 : STRING_LIST list;
1864 :
1865 0 : int status = mh->hs_get_events(time, &list);
1866 :
1867 0 : for (unsigned i=0; i<list.size(); i++) {
1868 0 : ss_repair_utf8(list[i]);
1869 0 : events->AddToArray(MJsonNode::MakeString(list[i].c_str()));
1870 : }
1871 :
1872 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "events", events);
1873 0 : }
1874 :
1875 0 : static MJsonNode* js_hs_reopen(const MJsonNode* params)
1876 : {
1877 0 : if (!params) {
1878 0 : MJSO* doc = MJSO::I();
1879 0 : doc->D("reopen the history channel to make sure we see the latest list of events using hs_clear_cache()");
1880 0 : doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
1881 0 : doc->R("status", MJSON_INT, "return status of hs_get_events()");
1882 0 : doc->R("channel", MJSON_STRING, "logger history channel name");
1883 0 : return doc;
1884 : }
1885 :
1886 0 : std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
1887 :
1888 0 : MidasHistoryInterface* mh = GetHistory(channel.c_str());
1889 :
1890 0 : if (!mh) {
1891 0 : int status = HS_FILE_ERROR;
1892 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
1893 : }
1894 :
1895 0 : int status = mh->hs_clear_cache();
1896 :
1897 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name));
1898 0 : }
1899 :
1900 0 : static MJsonNode* js_hs_get_tags(const MJsonNode* params)
1901 : {
1902 0 : if (!params) {
1903 0 : MJSO* doc = MJSO::I();
1904 0 : doc->D("get list of history tags for given history events that existed at give time using hs_get_tags()");
1905 0 : doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
1906 0 : doc->P("time?", MJSON_NUMBER, "timestamp, value 0 means current time, default is 0");
1907 0 : doc->P("events[]?", MJSON_STRING, "array of history event names, default is get all events using hs_get_events()");
1908 0 : doc->R("status", MJSON_INT, "return status");
1909 0 : doc->R("channel", MJSON_STRING, "logger history channel name");
1910 0 : doc->R("events[].name", MJSON_STRING, "array of history event names for each history event");
1911 0 : doc->R("events[].status", MJSON_INT, "array of status ohistory tags for each history event");
1912 0 : doc->R("events[].tags[]", MJSON_STRING, "array of history tags for each history event");
1913 0 : doc->R("events[].tags[].name", MJSON_STRING, "history tag name");
1914 0 : doc->R("events[].tags[].type", MJSON_INT, "history tag midas data type");
1915 0 : doc->R("events[].tags[].n_data?", MJSON_INT, "history tag number of array elements, omitted if 1");
1916 0 : return doc;
1917 : }
1918 :
1919 0 : std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
1920 0 : double time = mjsonrpc_get_param(params, "time", NULL)->GetDouble();
1921 0 : const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
1922 :
1923 0 : if (time == 0) {
1924 0 : time = ::time(NULL);
1925 : }
1926 :
1927 0 : MidasHistoryInterface* mh = GetHistory(channel.c_str());
1928 :
1929 0 : MJsonNode* events = MJsonNode::MakeArray();
1930 :
1931 0 : if (!mh) {
1932 0 : int status = HS_FILE_ERROR;
1933 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "events", events);
1934 : }
1935 :
1936 0 : std::vector<std::string> event_names;
1937 :
1938 0 : if (events_array && events_array->size() > 0) {
1939 0 : for (unsigned i=0; i<events_array->size(); i++) {
1940 0 : event_names.push_back((*events_array)[i]->GetString());
1941 : }
1942 : }
1943 :
1944 0 : if (event_names.size() < 1) {
1945 0 : int status = mh->hs_get_events(time, &event_names);
1946 0 : if (status != HS_SUCCESS) {
1947 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "events", events);
1948 : }
1949 : }
1950 :
1951 0 : for (unsigned i=0; i<event_names.size(); i++) {
1952 0 : MJsonNode* o = MJsonNode::MakeObject();
1953 0 : const char* event_name = event_names[i].c_str();
1954 0 : std::vector<TAG> tags;
1955 0 : int status = mh->hs_get_tags(event_name, time, &tags);
1956 : //ss_repair_utf8(event_name); redundant!
1957 0 : o->AddToObject("name", MJsonNode::MakeString(event_name));
1958 0 : o->AddToObject("status", MJsonNode::MakeInt(status));
1959 0 : MJsonNode *ta = MJsonNode::MakeArray();
1960 0 : for (unsigned j=0; j<tags.size(); j++) {
1961 0 : MJsonNode* to = MJsonNode::MakeObject();
1962 0 : ss_repair_utf8(tags[j].name);
1963 0 : to->AddToObject("name", MJsonNode::MakeString(tags[j].name));
1964 0 : to->AddToObject("type", MJsonNode::MakeInt(tags[j].type));
1965 0 : if (tags[j].n_data != 1) {
1966 0 : to->AddToObject("n_data", MJsonNode::MakeInt(tags[j].n_data));
1967 : }
1968 0 : ta->AddToArray(to);
1969 : }
1970 0 : o->AddToObject("tags", ta);
1971 0 : events->AddToArray(o);
1972 0 : }
1973 :
1974 0 : int status = HS_SUCCESS;
1975 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "events", events);
1976 0 : }
1977 :
1978 0 : static MJsonNode* js_hs_get_last_written(const MJsonNode* params)
1979 : {
1980 0 : if (!params) {
1981 0 : MJSO* doc = MJSO::I();
1982 0 : doc->D("get list of history tags for given history events that existed at give time using hs_get_last_written()");
1983 0 : doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
1984 0 : doc->P("time?", MJSON_NUMBER, "timestamp, value 0 means current time, default is 0");
1985 0 : doc->P("events[]", MJSON_STRING, "array of history event names");
1986 0 : doc->P("tags[]", MJSON_STRING, "array of history event tag names");
1987 0 : doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
1988 0 : doc->R("status", MJSON_INT, "return status");
1989 0 : doc->R("channel", MJSON_STRING, "logger history channel name");
1990 0 : doc->R("last_written[]", MJSON_NUMBER, "array of last-written times for each history event");
1991 0 : return doc;
1992 : }
1993 :
1994 0 : std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
1995 0 : double time = mjsonrpc_get_param(params, "time", NULL)->GetDouble();
1996 :
1997 0 : const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
1998 0 : const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
1999 0 : const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
2000 :
2001 0 : MidasHistoryInterface* mh = GetHistory(channel.c_str());
2002 :
2003 0 : MJsonNode* lw = MJsonNode::MakeArray();
2004 :
2005 0 : if (!mh) {
2006 0 : int status = HS_FILE_ERROR;
2007 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "last_written", lw);
2008 : }
2009 :
2010 0 : unsigned num_var = events_array->size();
2011 :
2012 0 : if (tags_array->size() != num_var) {
2013 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
2014 : }
2015 :
2016 0 : if (index_array->size() != num_var) {
2017 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
2018 : }
2019 :
2020 0 : std::vector<std::string> event_names(num_var);
2021 0 : std::vector<std::string> tag_names(num_var);
2022 : // const char** event_name = new const char*[num_var];
2023 : // const char** tag_name = new const char*[num_var];
2024 0 : int* var_index = new int[num_var];
2025 0 : time_t* last_written = new time_t[num_var];
2026 :
2027 0 : for (unsigned i=0; i<num_var; i++) {
2028 : //event_name[i] = (*events_array)[i]->GetString().c_str();
2029 : //tag_name[i] = (*tags_array)[i]->GetString().c_str();
2030 0 : event_names[i] = (*events_array)[i]->GetString();
2031 0 : tag_names[i] = (*tags_array)[i]->GetString();
2032 0 : var_index[i] = (*index_array)[i]->GetInt();
2033 : }
2034 :
2035 : if (/* DISABLES CODE */ (0)) {
2036 : printf("time %f, num_vars %d:\n", time, num_var);
2037 : for (unsigned i=0; i<num_var; i++) {
2038 : printf("%d: [%s] [%s] [%d]\n", i, event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
2039 : }
2040 : }
2041 :
2042 0 : if (time == 0) {
2043 0 : time = ::time(NULL);
2044 : }
2045 :
2046 :
2047 0 : const char** event_name = new const char*[num_var];
2048 0 : const char** tag_name = new const char*[num_var];
2049 0 : for (unsigned i=0; i<num_var; i++) {
2050 0 : event_name[i] = event_names[i].c_str();
2051 0 : tag_name[i] = tag_names[i].c_str();
2052 : }
2053 0 : int status = mh->hs_get_last_written(time, num_var, event_name, tag_name, var_index, last_written);
2054 :
2055 0 : for (unsigned i=0; i<num_var; i++) {
2056 : if (/* DISABLES CODE */ (0)) {
2057 : printf("%d: last_written %d\n", i, (int)last_written[i]);
2058 : }
2059 0 : lw->AddToArray(MJsonNode::MakeNumber(last_written[i]));
2060 : }
2061 :
2062 0 : delete[] event_name;
2063 0 : delete[] tag_name;
2064 0 : delete[] var_index;
2065 0 : delete[] last_written;
2066 :
2067 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "last_written", lw);
2068 0 : }
2069 :
2070 : class JsonHistoryBuffer: public MidasHistoryBufferInterface
2071 : {
2072 : public:
2073 : int fCount;
2074 : std::string fTimeJson;
2075 : std::string fValueJson;
2076 :
2077 : public:
2078 0 : JsonHistoryBuffer() // ctor
2079 0 : {
2080 0 : fCount = 0;
2081 :
2082 0 : fTimeJson = "[";
2083 0 : fValueJson = "[";
2084 0 : }
2085 :
2086 0 : void Add(time_t t, double v)
2087 : {
2088 : //printf("add time %d, value %f\n", (int)t, v);
2089 :
2090 0 : if (fCount>0) {
2091 0 : fTimeJson += ",";
2092 0 : fValueJson += ",";
2093 : }
2094 0 : fCount++;
2095 :
2096 0 : fTimeJson += MJsonNode::EncodeDouble(t);
2097 0 : fValueJson += MJsonNode::EncodeDouble(v);
2098 0 : }
2099 :
2100 0 : void Finish()
2101 : {
2102 0 : fTimeJson += "]";
2103 0 : fValueJson += "]";
2104 0 : }
2105 : };
2106 :
2107 0 : static MJsonNode* js_hs_read(const MJsonNode* params)
2108 : {
2109 0 : if (!params) {
2110 0 : MJSO* doc = MJSO::I();
2111 0 : doc->D("get history data for given history events that existed at give time using hs_read_buffer()");
2112 0 : doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
2113 0 : doc->P("start_time", MJSON_NUMBER, "start time of the data");
2114 0 : doc->P("end_time", MJSON_NUMBER, "end time of the data");
2115 0 : doc->P("events[]", MJSON_STRING, "array of history event names");
2116 0 : doc->P("tags[]", MJSON_STRING, "array of history event tag names");
2117 0 : doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
2118 0 : doc->R("status", MJSON_INT, "return status");
2119 0 : doc->R("channel", MJSON_STRING, "logger history channel name");
2120 0 : doc->R("data[]", MJSON_ARRAY, "array of history data");
2121 0 : doc->R("data[].status", MJSON_INT, "status for each event");
2122 0 : doc->R("data[].count", MJSON_INT, "number of data for each event");
2123 0 : doc->R("data[].time[]", MJSON_NUMBER, "time data");
2124 0 : doc->R("data[].value[]", MJSON_NUMBER, "value data");
2125 0 : return doc;
2126 : }
2127 :
2128 0 : MJsonNode* error = NULL;
2129 :
2130 0 : std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
2131 0 : double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
2132 0 : double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
2133 :
2134 0 : const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
2135 0 : const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
2136 0 : const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
2137 :
2138 0 : MidasHistoryInterface* mh = GetHistory(channel.c_str());
2139 :
2140 0 : MJsonNode* data = MJsonNode::MakeArray();
2141 :
2142 0 : if (!mh) {
2143 0 : int status = HS_FILE_ERROR;
2144 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "data", data);
2145 : }
2146 :
2147 0 : unsigned num_var = events_array->size();
2148 :
2149 0 : if (tags_array->size() != num_var) {
2150 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
2151 : }
2152 :
2153 0 : if (index_array->size() != num_var) {
2154 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
2155 : }
2156 :
2157 0 : std::vector<std::string> event_names(num_var);
2158 0 : std::vector<std::string> tag_names(num_var);
2159 0 : int* var_index = new int[num_var];
2160 0 : JsonHistoryBuffer** jbuf = new JsonHistoryBuffer*[num_var];
2161 0 : MidasHistoryBufferInterface** buf = new MidasHistoryBufferInterface*[num_var];
2162 0 : int* hs_status = new int[num_var];
2163 :
2164 0 : for (unsigned i=0; i<num_var; i++) {
2165 : //event_name[i] = (*events_array)[i]->GetString().c_str();
2166 : //tag_name[i] = (*tags_array)[i]->GetString().c_str();
2167 0 : event_names[i] = (*events_array)[i]->GetString();
2168 0 : tag_names[i] = (*tags_array)[i]->GetString();
2169 0 : var_index[i] = (*index_array)[i]->GetInt();
2170 0 : jbuf[i] = new JsonHistoryBuffer();
2171 0 : buf[i] = jbuf[i];
2172 0 : hs_status[i] = 0;
2173 : }
2174 :
2175 : if (/* DISABLES CODE */ (0)) {
2176 : printf("time %f %f, num_vars %d:\n", start_time, end_time, num_var);
2177 : for (unsigned i=0; i<num_var; i++) {
2178 : printf("%d: [%s] [%s] [%d]\n", i, event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
2179 : }
2180 : }
2181 :
2182 0 : const char** event_name = new const char*[num_var];
2183 0 : const char** tag_name = new const char*[num_var];
2184 0 : for (unsigned i=0; i<num_var; i++) {
2185 0 : event_name[i] = event_names[i].c_str();
2186 0 : tag_name[i] = tag_names[i].c_str();
2187 : }
2188 :
2189 0 : int status = mh->hs_read_buffer(start_time, end_time, num_var, event_name, tag_name, var_index, buf, hs_status);
2190 :
2191 0 : for (unsigned i=0; i<num_var; i++) {
2192 0 : jbuf[i]->Finish();
2193 :
2194 0 : MJsonNode* obj = MJsonNode::MakeObject();
2195 0 : obj->AddToObject("status", MJsonNode::MakeInt(hs_status[i]));
2196 0 : obj->AddToObject("count", MJsonNode::MakeInt(jbuf[i]->fCount));
2197 0 : obj->AddToObject("time", MJsonNode::MakeJSON(jbuf[i]->fTimeJson.c_str()));
2198 0 : obj->AddToObject("value", MJsonNode::MakeJSON(jbuf[i]->fValueJson.c_str()));
2199 0 : data->AddToArray(obj);
2200 :
2201 0 : delete jbuf[i];
2202 0 : jbuf[i] = NULL;
2203 0 : buf[i] = NULL;
2204 : }
2205 :
2206 0 : delete[] event_name;
2207 0 : delete[] tag_name;
2208 0 : delete[] var_index;
2209 0 : delete[] buf;
2210 0 : delete[] jbuf;
2211 0 : delete[] hs_status;
2212 :
2213 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "data", data);
2214 0 : }
2215 :
2216 0 : static MJsonNode* js_hs_read_binned(const MJsonNode* params)
2217 : {
2218 0 : if (!params) {
2219 0 : MJSO* doc = MJSO::I();
2220 0 : doc->D("get history data for given history events that existed at give time using hs_read_buffer()");
2221 0 : doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
2222 0 : doc->P("start_time", MJSON_NUMBER, "start time of the data");
2223 0 : doc->P("end_time", MJSON_NUMBER, "end time of the data");
2224 0 : doc->P("num_bins", MJSON_INT, "number of time bins");
2225 0 : doc->P("events[]", MJSON_STRING, "array of history event names");
2226 0 : doc->P("tags[]", MJSON_STRING, "array of history event tag names");
2227 0 : doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
2228 0 : doc->R("status", MJSON_INT, "return status");
2229 0 : doc->R("channel", MJSON_STRING, "logger history channel name");
2230 0 : doc->R("data[]", MJSON_ARRAY, "array of history data");
2231 0 : doc->R("data[].status", MJSON_INT, "status for each event");
2232 0 : doc->R("data[].num_entries", MJSON_INT, "number of data points for each event");
2233 0 : doc->R("data[].count[]", MJSON_INT, "number of data points for each bin");
2234 0 : doc->R("data[].mean[]", MJSON_NUMBER, "mean for each bin");
2235 0 : doc->R("data[].rms[]", MJSON_NUMBER, "rms for each bin");
2236 0 : doc->R("data[].min[]", MJSON_NUMBER, "minimum value for each bin");
2237 0 : doc->R("data[].max[]", MJSON_NUMBER, "maximum value for each bin");
2238 0 : doc->R("data[].bins_first_time[]", MJSON_NUMBER, "first data point in each bin");
2239 0 : doc->R("data[].bins_first_value[]", MJSON_NUMBER, "first data point in each bin");
2240 0 : doc->R("data[].bins_last_time[]", MJSON_NUMBER, "last data point in each bin");
2241 0 : doc->R("data[].bins_last_value[]", MJSON_NUMBER, "last data point in each bin");
2242 0 : doc->R("data[].last_time", MJSON_NUMBER, "time of last data entry");
2243 0 : doc->R("data[].last_value", MJSON_NUMBER, "value of last data entry");
2244 0 : return doc;
2245 : }
2246 :
2247 0 : MJsonNode* error = NULL;
2248 :
2249 0 : std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
2250 0 : double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
2251 0 : double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
2252 0 : int num_bins = mjsonrpc_get_param(params, "num_bins", &error)->GetInt(); if (error) return error;
2253 :
2254 0 : if (num_bins < 1) {
2255 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Value of num_bins should be 1 or more");
2256 : }
2257 :
2258 0 : const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
2259 0 : const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
2260 0 : const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
2261 :
2262 0 : MidasHistoryInterface* mh = GetHistory(channel.c_str());
2263 :
2264 0 : MJsonNode* data = MJsonNode::MakeArray();
2265 :
2266 0 : if (!mh) {
2267 0 : int status = HS_FILE_ERROR;
2268 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "data", data);
2269 : }
2270 :
2271 0 : unsigned num_var = events_array->size();
2272 :
2273 0 : if (num_var < 1) {
2274 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Array of events should have 1 or more elements");
2275 : }
2276 :
2277 0 : if (tags_array->size() != num_var) {
2278 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
2279 : }
2280 :
2281 0 : if (index_array->size() != num_var) {
2282 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
2283 : }
2284 :
2285 0 : std::vector<std::string> event_names(num_var);
2286 0 : std::vector<std::string> tag_names(num_var);
2287 : //const char** event_name = new const char*[num_var];
2288 : //const char** tag_name = new const char*[num_var];
2289 0 : int* var_index = new int[num_var];
2290 :
2291 0 : int* num_entries = new int[num_var];
2292 0 : time_t* last_time = new time_t[num_var];
2293 0 : double* last_value = new double[num_var];
2294 0 : int* hs_status = new int[num_var];
2295 :
2296 0 : int** count_bins = new int*[num_var];
2297 0 : double** mean_bins = new double*[num_var];
2298 0 : double** rms_bins = new double*[num_var];
2299 0 : double** min_bins = new double*[num_var];
2300 0 : double** max_bins = new double*[num_var];
2301 :
2302 0 : time_t** bins_first_time = new time_t*[num_var];
2303 0 : time_t** bins_last_time = new time_t*[num_var];
2304 :
2305 0 : double** bins_first_value = new double*[num_var];
2306 0 : double** bins_last_value = new double*[num_var];
2307 :
2308 0 : for (unsigned i=0; i<num_var; i++) {
2309 : //event_name[i] = (*events_array)[i]->GetString().c_str();
2310 : //tag_name[i] = (*tags_array)[i]->GetString().c_str();
2311 0 : event_names[i] = (*events_array)[i]->GetString();
2312 0 : tag_names[i] = (*tags_array)[i]->GetString();
2313 0 : var_index[i] = (*index_array)[i]->GetInt();
2314 0 : num_entries[i] = 0;
2315 0 : last_time[i] = 0;
2316 0 : last_value[i] = 0;
2317 0 : hs_status[i] = 0;
2318 0 : count_bins[i] = new int[num_bins];
2319 0 : mean_bins[i] = new double[num_bins];
2320 0 : rms_bins[i] = new double[num_bins];
2321 0 : min_bins[i] = new double[num_bins];
2322 0 : max_bins[i] = new double[num_bins];
2323 :
2324 0 : bins_first_time[i] = new time_t[num_bins];
2325 0 : bins_last_time[i] = new time_t[num_bins];
2326 :
2327 0 : bins_first_value[i] = new double[num_bins];
2328 0 : bins_last_value[i] = new double[num_bins];
2329 : }
2330 :
2331 : if (/* DISABLES CODE */ (0)) {
2332 : printf("time %f %f, num_vars %d:\n", start_time, end_time, num_var);
2333 : for (unsigned i=0; i<num_var; i++) {
2334 : printf("%d: [%s] [%s] [%d]\n", i, event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
2335 : }
2336 : }
2337 :
2338 0 : const char** event_name = new const char*[num_var];
2339 0 : const char** tag_name = new const char*[num_var];
2340 0 : for (unsigned i=0; i<num_var; i++) {
2341 0 : event_name[i] = event_names[i].c_str();
2342 0 : tag_name[i] = tag_names[i].c_str();
2343 : }
2344 :
2345 0 : int status = mh->hs_read_binned(start_time, end_time, num_bins, num_var, event_name, tag_name, var_index, num_entries, count_bins, mean_bins, rms_bins, min_bins, max_bins, bins_first_time, bins_first_value, bins_last_time, bins_last_value, last_time, last_value, hs_status);
2346 :
2347 0 : for (unsigned i=0; i<num_var; i++) {
2348 0 : MJsonNode* obj = MJsonNode::MakeObject();
2349 0 : obj->AddToObject("status", MJsonNode::MakeInt(hs_status[i]));
2350 0 : obj->AddToObject("num_entries", MJsonNode::MakeInt(num_entries[i]));
2351 :
2352 0 : MJsonNode* a1 = MJsonNode::MakeArray();
2353 0 : MJsonNode* a2 = MJsonNode::MakeArray();
2354 0 : MJsonNode* a3 = MJsonNode::MakeArray();
2355 0 : MJsonNode* a4 = MJsonNode::MakeArray();
2356 0 : MJsonNode* a5 = MJsonNode::MakeArray();
2357 :
2358 0 : MJsonNode* b1 = MJsonNode::MakeArray();
2359 0 : MJsonNode* b2 = MJsonNode::MakeArray();
2360 0 : MJsonNode* b3 = MJsonNode::MakeArray();
2361 0 : MJsonNode* b4 = MJsonNode::MakeArray();
2362 :
2363 0 : for (int j=0; j<num_bins; j++) {
2364 0 : a1->AddToArray(MJsonNode::MakeInt(count_bins[i][j]));
2365 0 : a2->AddToArray(MJsonNode::MakeNumber(mean_bins[i][j]));
2366 0 : a3->AddToArray(MJsonNode::MakeNumber(rms_bins[i][j]));
2367 0 : a4->AddToArray(MJsonNode::MakeNumber(min_bins[i][j]));
2368 0 : a5->AddToArray(MJsonNode::MakeNumber(max_bins[i][j]));
2369 :
2370 0 : b1->AddToArray(MJsonNode::MakeNumber(bins_first_time[i][j]));
2371 0 : b2->AddToArray(MJsonNode::MakeNumber(bins_first_value[i][j]));
2372 0 : b3->AddToArray(MJsonNode::MakeNumber(bins_last_time[i][j]));
2373 0 : b4->AddToArray(MJsonNode::MakeNumber(bins_last_value[i][j]));
2374 : }
2375 :
2376 0 : obj->AddToObject("count", a1);
2377 0 : obj->AddToObject("mean", a2);
2378 0 : obj->AddToObject("rms", a3);
2379 0 : obj->AddToObject("min", a4);
2380 0 : obj->AddToObject("max", a5);
2381 0 : obj->AddToObject("bins_first_time", b1);
2382 0 : obj->AddToObject("bins_first_value", b2);
2383 0 : obj->AddToObject("bins_last_time", b3);
2384 0 : obj->AddToObject("bins_last_value", b4);
2385 0 : obj->AddToObject("last_time", MJsonNode::MakeNumber(last_time[i]));
2386 0 : obj->AddToObject("last_value", MJsonNode::MakeNumber(last_value[i]));
2387 0 : data->AddToArray(obj);
2388 :
2389 0 : delete count_bins[i];
2390 0 : delete mean_bins[i];
2391 0 : delete rms_bins[i];
2392 0 : delete min_bins[i];
2393 0 : delete max_bins[i];
2394 :
2395 0 : delete bins_first_time[i];
2396 0 : delete bins_first_value[i];
2397 0 : delete bins_last_time[i];
2398 0 : delete bins_last_value[i];
2399 : }
2400 :
2401 0 : delete[] count_bins;
2402 0 : delete[] mean_bins;
2403 0 : delete[] rms_bins;
2404 0 : delete[] min_bins;
2405 0 : delete[] max_bins;
2406 :
2407 0 : delete[] bins_first_time;
2408 0 : delete[] bins_first_value;
2409 0 : delete[] bins_last_time;
2410 0 : delete[] bins_last_value;
2411 :
2412 0 : delete[] event_name;
2413 0 : delete[] tag_name;
2414 0 : delete[] var_index;
2415 :
2416 0 : delete[] num_entries;
2417 0 : delete[] last_time;
2418 0 : delete[] last_value;
2419 0 : delete[] hs_status;
2420 :
2421 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "channel", MJsonNode::MakeString(mh->name), "data", data);
2422 0 : }
2423 :
2424 : class BinaryHistoryBuffer: public MidasHistoryBufferInterface
2425 : {
2426 : public:
2427 : std::vector<double> fTimes;
2428 : std::vector<double> fValues;
2429 :
2430 : public:
2431 0 : BinaryHistoryBuffer() // ctor
2432 0 : {
2433 : // empty
2434 0 : }
2435 :
2436 0 : void Add(time_t t, double v)
2437 : {
2438 : //printf("add time %d, value %f\n", (int)t, v);
2439 :
2440 0 : fTimes.push_back(t);
2441 0 : fValues.push_back(v);
2442 0 : }
2443 :
2444 0 : void Finish()
2445 : {
2446 0 : assert(fTimes.size() == fValues.size());
2447 0 : }
2448 : };
2449 :
2450 0 : static MJsonNode* js_hs_read_arraybuffer(const MJsonNode* params)
2451 : {
2452 0 : if (!params) {
2453 0 : MJSO* doc = MJSO::I();
2454 0 : doc->D("get history data for given history events that existed at give time using hs_read_buffer()");
2455 0 : doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
2456 0 : doc->P("start_time", MJSON_NUMBER, "start time of the data");
2457 0 : doc->P("end_time", MJSON_NUMBER, "end time of the data");
2458 0 : doc->P("events[]", MJSON_STRING, "array of history event names");
2459 0 : doc->P("tags[]", MJSON_STRING, "array of history event tag names");
2460 0 : doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
2461 0 : doc->R("binary data", MJSON_ARRAYBUFFER, "binary data, see documentation");
2462 0 : return doc;
2463 : }
2464 :
2465 0 : MJsonNode* error = NULL;
2466 :
2467 0 : std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
2468 0 : double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
2469 0 : double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
2470 :
2471 0 : const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
2472 0 : const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
2473 0 : const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
2474 :
2475 0 : MidasHistoryInterface* mh = GetHistory(channel.c_str());
2476 :
2477 0 : if (!mh) {
2478 0 : int status = HS_FILE_ERROR;
2479 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
2480 : }
2481 :
2482 0 : size_t num_var = events_array->size();
2483 :
2484 0 : if (tags_array->size() != num_var) {
2485 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
2486 : }
2487 :
2488 0 : if (index_array->size() != num_var) {
2489 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
2490 : }
2491 :
2492 0 : std::vector<std::string> event_names(num_var);
2493 0 : std::vector<std::string> tag_names(num_var);
2494 0 : int* var_index = new int[num_var];
2495 0 : BinaryHistoryBuffer** jbuf = new BinaryHistoryBuffer*[num_var];
2496 0 : MidasHistoryBufferInterface** buf = new MidasHistoryBufferInterface*[num_var];
2497 0 : int* hs_status = new int[num_var];
2498 :
2499 0 : for (size_t i=0; i<num_var; i++) {
2500 : //event_name[i] = (*events_array)[i]->GetString().c_str();
2501 : //tag_name[i] = (*tags_array)[i]->GetString().c_str();
2502 0 : event_names[i] = (*events_array)[i]->GetString();
2503 0 : tag_names[i] = (*tags_array)[i]->GetString();
2504 0 : var_index[i] = (*index_array)[i]->GetInt();
2505 0 : jbuf[i] = new BinaryHistoryBuffer();
2506 0 : buf[i] = jbuf[i];
2507 0 : hs_status[i] = 0;
2508 : }
2509 :
2510 : if (/* DISABLES CODE */ (0)) {
2511 : printf("time %f %f, num_vars %d:\n", start_time, end_time, int(num_var));
2512 : for (size_t i=0; i<num_var; i++) {
2513 : printf("%d: [%s] [%s] [%d]\n", int(i), event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
2514 : }
2515 : }
2516 :
2517 0 : const char** event_name = new const char*[num_var];
2518 0 : const char** tag_name = new const char*[num_var];
2519 0 : for (unsigned i=0; i<num_var; i++) {
2520 0 : event_name[i] = event_names[i].c_str();
2521 0 : tag_name[i] = tag_names[i].c_str();
2522 : }
2523 :
2524 0 : int status = mh->hs_read_buffer(start_time, end_time, num_var, event_name, tag_name, var_index, buf, hs_status);
2525 :
2526 0 : size_t num_values = 0;
2527 :
2528 0 : for (unsigned i=0; i<num_var; i++) {
2529 0 : jbuf[i]->Finish();
2530 0 : num_values += jbuf[i]->fValues.size();
2531 : }
2532 :
2533 : // NB: beware of 32-bit integer overflow. all values are now 64-bit size_t, overflow should not happen.
2534 0 : size_t p0_size = sizeof(double)*(2+2*num_var+2*num_values);
2535 :
2536 0 : size_t size_limit = 1000*1024*1024;
2537 :
2538 0 : if (p0_size > size_limit) {
2539 0 : cm_msg(MERROR, "js_hs_read_binned_arraybuffer", "Refusing to return %zu bytes of history data, limit is %zu bytes\n", p0_size, size_limit);
2540 :
2541 0 : for (size_t i=0; i<num_var; i++) {
2542 0 : delete jbuf[i];
2543 0 : jbuf[i] = NULL;
2544 0 : buf[i] = NULL;
2545 : }
2546 :
2547 0 : delete[] event_name;
2548 0 : delete[] tag_name;
2549 0 : delete[] var_index;
2550 0 : delete[] buf;
2551 0 : delete[] jbuf;
2552 0 : delete[] hs_status;
2553 :
2554 0 : return mjsonrpc_make_error(-32603, "Internal error", "Too much history data");
2555 : }
2556 :
2557 0 : double* p0 = (double*)malloc(p0_size);
2558 :
2559 0 : if (p0 == NULL) {
2560 0 : cm_msg(MERROR, "js_hs_read_binned_arraybuffer", "Cannot allocate return buffer %d bytes\n", int(p0_size));
2561 :
2562 0 : for (size_t i=0; i<num_var; i++) {
2563 0 : delete jbuf[i];
2564 0 : jbuf[i] = NULL;
2565 0 : buf[i] = NULL;
2566 : }
2567 :
2568 0 : delete[] event_name;
2569 0 : delete[] tag_name;
2570 0 : delete[] var_index;
2571 0 : delete[] buf;
2572 0 : delete[] jbuf;
2573 0 : delete[] hs_status;
2574 :
2575 0 : return mjsonrpc_make_error(-32603, "Internal error", "Cannot allocate buffer, too much data");
2576 : }
2577 :
2578 0 : double *pptr = p0;
2579 :
2580 : //
2581 : // Binary data format:
2582 : //
2583 : // - hs_read() status
2584 : // - num_var
2585 : // - hs_status[0..num_var-1]
2586 : // - num_values[0..num_var-1]
2587 : // - data for var0:
2588 : // - t[0][0], v[0][0] ... t[0][num_values[0]-1], v[0][num_values[0]-1]
2589 : // - data for var1:
2590 : // - t[1][0], v[1][0] ... t[1][num_values[1]-1], v[1][num_values[1]-1]
2591 : // ...
2592 : // - data for last variable:
2593 : // - t[num_var-1][0], v[num_var-1][0] ... t[num_var-1][num_values[num_var-1]-1], v[num_var-1][num_values[num_var-1]-1]
2594 : //
2595 :
2596 0 : *pptr++ = status;
2597 0 : *pptr++ = num_var;
2598 :
2599 0 : for (size_t i=0; i<num_var; i++) {
2600 0 : *pptr++ = hs_status[i];
2601 : }
2602 :
2603 0 : for (size_t i=0; i<num_var; i++) {
2604 0 : *pptr++ = jbuf[i]->fValues.size();
2605 : }
2606 :
2607 0 : for (size_t i=0; i<num_var; i++) {
2608 0 : size_t nv = jbuf[i]->fValues.size();
2609 0 : for (size_t j=0; j<nv; j++) {
2610 0 : *pptr++ = jbuf[i]->fTimes[j];
2611 0 : *pptr++ = jbuf[i]->fValues[j];
2612 : }
2613 :
2614 0 : delete jbuf[i];
2615 0 : jbuf[i] = NULL;
2616 0 : buf[i] = NULL;
2617 : }
2618 :
2619 : //printf("p0_size %d, %d/%d\n", (int)p0_size, (int)(pptr-p0), (int)((pptr-p0)*sizeof(double)));
2620 :
2621 0 : assert(p0_size == ((pptr-p0)*sizeof(double)));
2622 :
2623 0 : delete[] event_name;
2624 0 : delete[] tag_name;
2625 0 : delete[] var_index;
2626 0 : delete[] buf;
2627 0 : delete[] jbuf;
2628 0 : delete[] hs_status;
2629 :
2630 0 : MJsonNode* result = MJsonNode::MakeArrayBuffer((char*)p0, p0_size);
2631 :
2632 0 : return result;
2633 0 : }
2634 :
2635 0 : static MJsonNode* js_hs_read_binned_arraybuffer(const MJsonNode* params)
2636 : {
2637 0 : if (!params) {
2638 0 : MJSO* doc = MJSO::I();
2639 0 : doc->D("get history data for given history events that existed at give time using hs_read_buffer()");
2640 0 : doc->P("channel?", MJSON_STRING, "midas history channel, default is the default reader channel");
2641 0 : doc->P("start_time", MJSON_NUMBER, "start time of the data");
2642 0 : doc->P("end_time", MJSON_NUMBER, "end time of the data");
2643 0 : doc->P("num_bins", MJSON_INT, "number of time bins");
2644 0 : doc->P("events[]", MJSON_STRING, "array of history event names");
2645 0 : doc->P("tags[]", MJSON_STRING, "array of history event tag names");
2646 0 : doc->P("index[]", MJSON_STRING, "array of history event tag array indices");
2647 0 : doc->R("binary data", MJSON_ARRAYBUFFER, "binary data, see documentation");
2648 0 : return doc;
2649 : }
2650 :
2651 0 : MJsonNode* error = NULL;
2652 :
2653 0 : std::string channel = mjsonrpc_get_param(params, "channel", NULL)->GetString();
2654 0 : double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
2655 0 : double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
2656 0 : int inum_bins = mjsonrpc_get_param(params, "num_bins", &error)->GetInt(); if (error) return error;
2657 :
2658 0 : if (inum_bins < 1) {
2659 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Value of num_bins should be 1 or more");
2660 : }
2661 :
2662 0 : size_t num_bins = inum_bins;
2663 :
2664 0 : const MJsonNodeVector* events_array = mjsonrpc_get_param_array(params, "events", NULL);
2665 0 : const MJsonNodeVector* tags_array = mjsonrpc_get_param_array(params, "tags", NULL);
2666 0 : const MJsonNodeVector* index_array = mjsonrpc_get_param_array(params, "index", NULL);
2667 :
2668 0 : MidasHistoryInterface* mh = GetHistory(channel.c_str());
2669 :
2670 0 : if (!mh) {
2671 0 : int status = HS_FILE_ERROR;
2672 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
2673 : }
2674 :
2675 0 : size_t num_var = events_array->size();
2676 :
2677 0 : if (num_var < 1) {
2678 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Array of events should have 1 or more elements");
2679 : }
2680 :
2681 0 : if (tags_array->size() != num_var) {
2682 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and tags should have the same length");
2683 : }
2684 :
2685 0 : if (index_array->size() != num_var) {
2686 0 : return mjsonrpc_make_error(-32602, "Invalid params", "Arrays events and index should have the same length");
2687 : }
2688 :
2689 0 : std::vector<std::string> event_names(num_var);
2690 0 : std::vector<std::string> tag_names(num_var);
2691 : //const char** event_name = new const char*[num_var];
2692 : //const char** tag_name = new const char*[num_var];
2693 0 : int* var_index = new int[num_var];
2694 :
2695 0 : int* num_entries = new int[num_var];
2696 0 : time_t* last_time = new time_t[num_var];
2697 0 : double* last_value = new double[num_var];
2698 0 : int* hs_status = new int[num_var];
2699 :
2700 0 : int** count_bins = new int*[num_var];
2701 0 : double** mean_bins = new double*[num_var];
2702 0 : double** rms_bins = new double*[num_var];
2703 0 : double** min_bins = new double*[num_var];
2704 0 : double** max_bins = new double*[num_var];
2705 :
2706 0 : time_t** bins_first_time = new time_t*[num_var];
2707 0 : time_t** bins_last_time = new time_t*[num_var];
2708 :
2709 0 : double** bins_first_value = new double*[num_var];
2710 0 : double** bins_last_value = new double*[num_var];
2711 :
2712 0 : for (unsigned i=0; i<num_var; i++) {
2713 : //event_name[i] = (*events_array)[i]->GetString().c_str();
2714 : //tag_name[i] = (*tags_array)[i]->GetString().c_str();
2715 0 : event_names[i] = (*events_array)[i]->GetString();
2716 0 : tag_names[i] = (*tags_array)[i]->GetString();
2717 0 : var_index[i] = (*index_array)[i]->GetInt();
2718 0 : num_entries[i] = 0;
2719 0 : last_time[i] = 0;
2720 0 : last_value[i] = 0;
2721 0 : hs_status[i] = 0;
2722 0 : count_bins[i] = new int[num_bins];
2723 0 : mean_bins[i] = new double[num_bins];
2724 0 : rms_bins[i] = new double[num_bins];
2725 0 : min_bins[i] = new double[num_bins];
2726 0 : max_bins[i] = new double[num_bins];
2727 0 : bins_first_time[i] = new time_t[num_bins];
2728 0 : bins_last_time[i] = new time_t[num_bins];
2729 0 : bins_first_value[i] = new double[num_bins];
2730 0 : bins_last_value[i] = new double[num_bins];
2731 : }
2732 :
2733 : if (/* DISABLES CODE */ (0)) {
2734 : printf("time %f %f, num_vars %d:\n", start_time, end_time, int(num_var));
2735 : for (size_t i=0; i<num_var; i++) {
2736 : printf("%d: [%s] [%s] [%d]\n", int(i), event_names[i].c_str(), tag_names[i].c_str(), var_index[i]);
2737 : }
2738 : }
2739 :
2740 0 : const char** event_name = new const char*[num_var];
2741 0 : const char** tag_name = new const char*[num_var];
2742 0 : for (size_t i=0; i<num_var; i++) {
2743 0 : event_name[i] = event_names[i].c_str();
2744 0 : tag_name[i] = tag_names[i].c_str();
2745 : }
2746 :
2747 0 : int status = mh->hs_read_binned(start_time, end_time, num_bins, num_var, event_name, tag_name, var_index, num_entries, count_bins, mean_bins, rms_bins, min_bins, max_bins, bins_first_time, bins_first_value, bins_last_time, bins_last_value, last_time, last_value, hs_status);
2748 :
2749 : // NB: beware of 32-bit integer overflow: all variables are now 64-bit size_t, overflow should not happen
2750 0 : size_t p0_size = sizeof(double)*(5+4*num_var+9*num_var*num_bins);
2751 :
2752 0 : size_t size_limit = 100*1024*1024;
2753 :
2754 0 : if (p0_size > size_limit) {
2755 0 : cm_msg(MERROR, "js_hs_read_binned_arraybuffer", "Refusing to return %d bytes. limit is %d bytes\n", int(p0_size), int(size_limit));
2756 :
2757 0 : for (size_t i=0; i<num_var; i++) {
2758 0 : delete count_bins[i];
2759 0 : delete mean_bins[i];
2760 0 : delete rms_bins[i];
2761 0 : delete min_bins[i];
2762 0 : delete max_bins[i];
2763 0 : delete bins_first_time[i];
2764 0 : delete bins_first_value[i];
2765 0 : delete bins_last_time[i];
2766 0 : delete bins_last_value[i];
2767 : }
2768 :
2769 0 : delete[] count_bins;
2770 0 : delete[] mean_bins;
2771 0 : delete[] rms_bins;
2772 0 : delete[] min_bins;
2773 0 : delete[] max_bins;
2774 :
2775 0 : delete[] bins_first_time;
2776 0 : delete[] bins_first_value;
2777 0 : delete[] bins_last_time;
2778 0 : delete[] bins_last_value;
2779 :
2780 0 : delete[] event_name;
2781 0 : delete[] tag_name;
2782 0 : delete[] var_index;
2783 :
2784 0 : delete[] num_entries;
2785 0 : delete[] last_time;
2786 0 : delete[] last_value;
2787 0 : delete[] hs_status;
2788 :
2789 0 : return mjsonrpc_make_error(-32603, "Internal error", "Refuse to return too much data");
2790 : }
2791 :
2792 0 : double* p0 = (double*)malloc(p0_size);
2793 :
2794 0 : if (p0 == NULL) {
2795 0 : cm_msg(MERROR, "js_hs_read_binned_arraybuffer", "Cannot allocate return buffer %d bytes\n", int(p0_size));
2796 :
2797 0 : for (size_t i=0; i<num_var; i++) {
2798 0 : delete count_bins[i];
2799 0 : delete mean_bins[i];
2800 0 : delete rms_bins[i];
2801 0 : delete min_bins[i];
2802 0 : delete max_bins[i];
2803 0 : delete bins_first_time[i];
2804 0 : delete bins_first_value[i];
2805 0 : delete bins_last_time[i];
2806 0 : delete bins_last_value[i];
2807 : }
2808 :
2809 0 : delete[] count_bins;
2810 0 : delete[] mean_bins;
2811 0 : delete[] rms_bins;
2812 0 : delete[] min_bins;
2813 0 : delete[] max_bins;
2814 :
2815 0 : delete[] bins_first_time;
2816 0 : delete[] bins_first_value;
2817 0 : delete[] bins_last_time;
2818 0 : delete[] bins_last_value;
2819 :
2820 0 : delete[] event_name;
2821 0 : delete[] tag_name;
2822 0 : delete[] var_index;
2823 :
2824 0 : delete[] num_entries;
2825 0 : delete[] last_time;
2826 0 : delete[] last_value;
2827 0 : delete[] hs_status;
2828 :
2829 0 : return mjsonrpc_make_error(-32603, "Internal error", "Cannot allocate buffer, too much data");
2830 : }
2831 :
2832 0 : double *pptr = p0;
2833 :
2834 : //
2835 : // Binary data format:
2836 : //
2837 : // * header
2838 : // -- hs_read() status
2839 : // -- start_time
2840 : // -- end_time
2841 : // -- num_bins
2842 : // -- num_var
2843 : // * per variable info
2844 : // -- hs_status[0..num_var-1]
2845 : // -- num_entries[0..num_var-1]
2846 : // -- last_time[0..num_var-1]
2847 : // -- last_value[0..num_var-1]
2848 : // * data for var0 bin0
2849 : // -- count - number of entries in this bin
2850 : // -- mean - mean value
2851 : // -- rms - rms value
2852 : // -- min - minimum value
2853 : // -- max - maximum value
2854 : // -- bins_first_time - first data point in each bin
2855 : // -- bins_first_value
2856 : // -- bins_last_time - last data point in each bin
2857 : // -- bins_last_value
2858 : // - data for var0 bin1
2859 : // - ... bin[num_bins-1]
2860 : // - data for var1 bin0
2861 : // - ...
2862 : // - data for var[num_vars-1] bin[0]
2863 : // - ...
2864 : // - data for var[num_vars-1] bin[num_bins-1]
2865 : //
2866 :
2867 0 : *pptr++ = status;
2868 0 : *pptr++ = start_time;
2869 0 : *pptr++ = end_time;
2870 0 : *pptr++ = num_bins;
2871 0 : *pptr++ = num_var;
2872 :
2873 0 : for (unsigned i=0; i<num_var; i++) {
2874 0 : *pptr++ = hs_status[i];
2875 : }
2876 :
2877 0 : for (unsigned i=0; i<num_var; i++) {
2878 0 : *pptr++ = num_entries[i];
2879 : }
2880 :
2881 0 : for (unsigned i=0; i<num_var; i++) {
2882 0 : *pptr++ = last_time[i];
2883 : }
2884 :
2885 0 : for (unsigned i=0; i<num_var; i++) {
2886 0 : *pptr++ = last_value[i];
2887 : }
2888 :
2889 0 : for (size_t i=0; i<num_var; i++) {
2890 0 : for (size_t j=0; j<num_bins; j++) {
2891 0 : *pptr++ = count_bins[i][j];
2892 0 : *pptr++ = mean_bins[i][j];
2893 0 : *pptr++ = rms_bins[i][j];
2894 0 : *pptr++ = min_bins[i][j];
2895 0 : *pptr++ = max_bins[i][j];
2896 0 : *pptr++ = bins_first_time[i][j];
2897 0 : *pptr++ = bins_first_value[i][j];
2898 0 : *pptr++ = bins_last_time[i][j];
2899 0 : *pptr++ = bins_last_value[i][j];
2900 : }
2901 :
2902 0 : delete count_bins[i];
2903 0 : delete mean_bins[i];
2904 0 : delete rms_bins[i];
2905 0 : delete min_bins[i];
2906 0 : delete max_bins[i];
2907 0 : delete bins_first_time[i];
2908 0 : delete bins_first_value[i];
2909 0 : delete bins_last_time[i];
2910 0 : delete bins_last_value[i];
2911 : }
2912 :
2913 : //printf("p0_size %d, %d/%d\n", (int)p0_size, (int)(pptr-p0), (int)((pptr-p0)*sizeof(double)));
2914 :
2915 0 : assert(p0_size == ((pptr-p0)*sizeof(double)));
2916 :
2917 0 : delete[] count_bins;
2918 0 : delete[] mean_bins;
2919 0 : delete[] rms_bins;
2920 0 : delete[] min_bins;
2921 0 : delete[] max_bins;
2922 :
2923 0 : delete[] bins_first_time;
2924 0 : delete[] bins_first_value;
2925 0 : delete[] bins_last_time;
2926 0 : delete[] bins_last_value;
2927 :
2928 0 : delete[] event_name;
2929 0 : delete[] tag_name;
2930 0 : delete[] var_index;
2931 :
2932 0 : delete[] num_entries;
2933 0 : delete[] last_time;
2934 0 : delete[] last_value;
2935 0 : delete[] hs_status;
2936 :
2937 0 : MJsonNode* result = MJsonNode::MakeArrayBuffer((char*)p0, p0_size);
2938 :
2939 0 : return result;
2940 0 : }
2941 :
2942 : /////////////////////////////////////////////////////////////////////////////////
2943 : //
2944 : // image history code goes here
2945 : //
2946 : /////////////////////////////////////////////////////////////////////////////////
2947 :
2948 0 : static MJsonNode* js_hs_image_retrieve(const MJsonNode* params) {
2949 0 : if (!params) {
2950 0 : MJSO *doc = MJSO::I();
2951 0 : doc->D("Get a list of history image files");
2952 0 : doc->P("image?", MJSON_STRING, "image name as defined under /History/Images/<image>");
2953 0 : doc->P("start_time", MJSON_NUMBER, "start time of the data");
2954 0 : doc->P("end_time", MJSON_NUMBER, "end time of the data");
2955 0 : doc->R("time[]", MJSON_ARRAYBUFFER, "array of time stamps in seconds");
2956 0 : doc->R("filename[]", MJSON_ARRAYBUFFER, "array of file names");
2957 0 : return doc;
2958 : }
2959 :
2960 0 : MJsonNode* error = NULL;
2961 :
2962 0 : std::string image = mjsonrpc_get_param(params, "image", NULL)->GetString();
2963 0 : double start_time = mjsonrpc_get_param(params, "start_time", &error)->GetDouble(); if (error) return error;
2964 0 : double end_time = mjsonrpc_get_param(params, "end_time", &error)->GetDouble(); if (error) return error;
2965 :
2966 0 : std::vector<time_t>vtime{};
2967 0 : std::vector<std::string>vfilename{};
2968 :
2969 0 : int status = hs_image_retrieve(image, start_time, end_time, vtime, vfilename);
2970 0 : int count = 10;
2971 0 : MJsonNode *tj = MJsonNode::MakeArray();
2972 0 : MJsonNode *fj = MJsonNode::MakeArray();
2973 :
2974 0 : for (int i=0 ; i<(int)vtime.size() ; i++) {
2975 0 : tj->AddToArray(MJsonNode::MakeInt(vtime[i]));
2976 0 : ss_repair_utf8(vfilename[i]);
2977 0 : fj->AddToArray(MJsonNode::MakeString(vfilename[i].c_str()));
2978 : }
2979 0 : MJsonNode* data = MJsonNode::MakeObject();
2980 0 : data->AddToObject("count", MJsonNode::MakeInt(count));
2981 0 : data->AddToObject("time", tj);
2982 0 : data->AddToObject("filename", fj);
2983 :
2984 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "data", data);
2985 0 : }
2986 :
2987 : /////////////////////////////////////////////////////////////////////////////////
2988 : //
2989 : // elog code goes here
2990 : //
2991 : /////////////////////////////////////////////////////////////////////////////////
2992 :
2993 0 : static MJsonNode* js_el_retrieve(const MJsonNode* params)
2994 : {
2995 0 : if (!params) {
2996 0 : MJSO* doc = MJSO::I();
2997 0 : doc->D("Get an elog message");
2998 0 : doc->P("tag", MJSON_STRING, "elog message tag");
2999 0 : doc->R("status", MJSON_INT, "return status of el_retrieve");
3000 0 : doc->R("msg.tag", MJSON_STRING, "message tag");
3001 0 : return doc;
3002 : }
3003 :
3004 0 : MJsonNode* error = NULL;
3005 :
3006 0 : std::string tag = mjsonrpc_get_param(params, "tag", &error)->GetString(); if (error) return error;
3007 :
3008 0 : int run = 0;
3009 : char date[80], author[80], type[80], system[80], subject[256], text[10000];
3010 : char orig_tag[80], reply_tag[80], attachment[3][256], encoding[80];
3011 :
3012 : char xtag[80];
3013 0 : mstrlcpy(xtag, tag.c_str(), sizeof(xtag));
3014 :
3015 0 : int size = sizeof(text);
3016 :
3017 0 : int status = el_retrieve(xtag,
3018 : date, &run, author, type, system, subject,
3019 : text, &size, orig_tag, reply_tag,
3020 : attachment[0], attachment[1], attachment[2], encoding);
3021 :
3022 : //printf("js_el_retrieve: size %d, status %d, tag [%s]\n", size, status, xtag);
3023 :
3024 0 : MJsonNode* msg = MJsonNode::MakeObject();
3025 :
3026 0 : if (status == EL_SUCCESS) {
3027 0 : ss_repair_utf8(xtag);
3028 0 : msg->AddToObject("tag", MJsonNode::MakeString(xtag));
3029 0 : ss_repair_utf8(date);
3030 0 : msg->AddToObject("date", MJsonNode::MakeString(date));
3031 0 : msg->AddToObject("run", MJsonNode::MakeInt(run));
3032 0 : ss_repair_utf8(author);
3033 0 : msg->AddToObject("author", MJsonNode::MakeString(author));
3034 0 : ss_repair_utf8(type);
3035 0 : msg->AddToObject("type", MJsonNode::MakeString(type));
3036 0 : ss_repair_utf8(system);
3037 0 : msg->AddToObject("system", MJsonNode::MakeString(system));
3038 0 : ss_repair_utf8(subject);
3039 0 : msg->AddToObject("subject", MJsonNode::MakeString(subject));
3040 0 : ss_repair_utf8(text);
3041 0 : msg->AddToObject("text", MJsonNode::MakeString(text));
3042 0 : ss_repair_utf8(orig_tag);
3043 0 : msg->AddToObject("orig_tag", MJsonNode::MakeString(orig_tag));
3044 0 : ss_repair_utf8(reply_tag);
3045 0 : msg->AddToObject("reply_tag", MJsonNode::MakeString(reply_tag));
3046 0 : ss_repair_utf8(attachment[0]);
3047 0 : msg->AddToObject("attachment0", MJsonNode::MakeString(attachment[0]));
3048 0 : ss_repair_utf8(attachment[1]);
3049 0 : msg->AddToObject("attachment1", MJsonNode::MakeString(attachment[1]));
3050 0 : ss_repair_utf8(attachment[2]);
3051 0 : msg->AddToObject("attachment2", MJsonNode::MakeString(attachment[2]));
3052 0 : ss_repair_utf8(encoding);
3053 0 : msg->AddToObject("encoding", MJsonNode::MakeString(encoding));
3054 : }
3055 :
3056 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "msg", msg);
3057 0 : }
3058 :
3059 0 : static MJsonNode* js_el_query(const MJsonNode* params)
3060 : {
3061 0 : if (!params) {
3062 0 : MJSO* doc = MJSO::I();
3063 0 : doc->D("Query elog messages");
3064 0 : doc->P("last_n_hours?", MJSON_INT, "return messages from the last N hours");
3065 0 : doc->R("status", MJSON_INT, "return status of el_retrieve");
3066 0 : doc->R("msg[].tag", MJSON_STRING, "message tag");
3067 0 : return doc;
3068 : }
3069 :
3070 : //MJsonNode* error = NULL;
3071 :
3072 : //int last_n = mjsonrpc_get_param(params, "last_n_hours", &error)->GetInt(); if (error) return error;
3073 0 : int last_n = mjsonrpc_get_param(params, "last_n_hours", NULL)->GetInt();
3074 :
3075 0 : std::string pd1 = mjsonrpc_get_param(params, "d1", NULL)->GetString();
3076 0 : std::string pm1 = mjsonrpc_get_param(params, "m1", NULL)->GetString();
3077 0 : std::string py1 = mjsonrpc_get_param(params, "y1", NULL)->GetString();
3078 :
3079 0 : std::string pd2 = mjsonrpc_get_param(params, "d2", NULL)->GetString();
3080 0 : std::string pm2 = mjsonrpc_get_param(params, "m2", NULL)->GetString();
3081 0 : std::string py2 = mjsonrpc_get_param(params, "y2", NULL)->GetString();
3082 :
3083 0 : std::string pr1 = mjsonrpc_get_param(params, "r1", NULL)->GetString();
3084 0 : std::string pr2 = mjsonrpc_get_param(params, "r2", NULL)->GetString();
3085 :
3086 0 : std::string ptype = mjsonrpc_get_param(params, "type", NULL)->GetString();
3087 0 : std::string psystem = mjsonrpc_get_param(params, "system", NULL)->GetString();
3088 0 : std::string pauthor = mjsonrpc_get_param(params, "author", NULL)->GetString();
3089 0 : std::string psubject = mjsonrpc_get_param(params, "subject", NULL)->GetString();
3090 0 : std::string psubtext = mjsonrpc_get_param(params, "subtext", NULL)->GetString();
3091 :
3092 0 : MJsonNode* msg_array = MJsonNode::MakeArray();
3093 :
3094 : int i, size, run, status;
3095 : char date[80], author[80], type[80], system[80], subject[256], text[10000],
3096 : orig_tag[80], reply_tag[80], attachment[3][256], encoding[80];
3097 : char str[256], str2[10000], tag[256];
3098 : struct tm tms;
3099 :
3100 : // month name from midas.c
3101 : extern const char *mname[];
3102 :
3103 : /*---- convert end date to ltime ----*/
3104 :
3105 0 : int y1 = -1;
3106 0 : int m1 = -1;
3107 0 : int d1 = -1;
3108 :
3109 0 : int y2 = -1;
3110 0 : int m2 = -1;
3111 0 : int d2 = -1;
3112 :
3113 0 : int r1 = -1;
3114 0 : int r2 = -1;
3115 :
3116 0 : if (pr1.length()>0)
3117 0 : r1 = atoi(pr1.c_str());
3118 :
3119 0 : if (pr2.length()>0)
3120 0 : r2 = atoi(pr2.c_str());
3121 :
3122 0 : time_t ltime_start = 0;
3123 0 : time_t ltime_end = 0;
3124 :
3125 0 : if (!last_n) {
3126 : // decode starting date year, day and month
3127 :
3128 0 : if (py1.length() > 0)
3129 0 : y1 = atoi(py1.c_str());
3130 :
3131 0 : if (pd1.length() > 0)
3132 0 : d1 = atoi(pd1.c_str());
3133 :
3134 0 : mstrlcpy(str, pm1.c_str(), sizeof(str));
3135 0 : for (m1 = 0; m1 < 12; m1++)
3136 0 : if (equal_ustring(str, mname[m1]))
3137 0 : break;
3138 0 : if (m1 == 12)
3139 0 : m1 = 0;
3140 :
3141 0 : if (pd2.length() > 0) {
3142 0 : d2 = atoi(pd2.c_str());
3143 : }
3144 :
3145 0 : if (py2.length() > 0) {
3146 : // decode ending date year, day and month
3147 :
3148 0 : mstrlcpy(str, pm2.c_str(), sizeof(str));
3149 0 : for (m2 = 0; m2 < 12; m2++)
3150 0 : if (equal_ustring(str, mname[m2]))
3151 0 : break;
3152 0 : if (m2 == 12) {
3153 0 : m2 = 0;
3154 : }
3155 : }
3156 :
3157 0 : if (py2.length() > 0) {
3158 0 : y2 = atoi(py2.c_str());
3159 : }
3160 :
3161 0 : if (y2>=0 && m2>=0 && d2>=0) {
3162 0 : memset(&tms, 0, sizeof(struct tm));
3163 0 : tms.tm_year = y2 % 100;
3164 0 : tms.tm_mon = m2;
3165 0 : tms.tm_mday = d2;
3166 0 : tms.tm_hour = 24;
3167 :
3168 0 : if (tms.tm_year < 90)
3169 0 : tms.tm_year += 100;
3170 0 : ltime_end = ss_mktime(&tms);
3171 : }
3172 : }
3173 :
3174 : /*---- do query ----*/
3175 :
3176 0 : tag[0] = 0;
3177 :
3178 0 : if (last_n) {
3179 0 : ss_tzset(); // required for localtime_r()
3180 0 : time_t now = time(NULL);
3181 0 : ltime_start = now - 3600 * last_n;
3182 : struct tm tms;
3183 0 : localtime_r(<ime_start, &tms);
3184 0 : sprintf(tag, "%02d%02d%02d.0", tms.tm_year % 100, tms.tm_mon + 1, tms.tm_mday);
3185 0 : } else if (r1 > 0) {
3186 : /* do run query */
3187 0 : el_search_run(r1, tag);
3188 0 : } else if (y1>=0 && m1>=0 && d1>=0) {
3189 : /* do date-date query */
3190 0 : sprintf(tag, "%02d%02d%02d.0", y1 % 100, m1 + 1, d1);
3191 : }
3192 :
3193 : #if 0
3194 : printf("js_el_query: y1 %d, m1 %d, d1 %d, y2 %d, m2 %d, d2 %d, r1 %d, r2 %d, last_n_hours %d, start time %lu, end time %lu, tag [%s]\n",
3195 : y1, m1, d1,
3196 : y2, m2, d2,
3197 : r1, r2,
3198 : last_n,
3199 : ltime_start,
3200 : ltime_end,
3201 : tag);
3202 : #endif
3203 :
3204 : do {
3205 0 : size = sizeof(text);
3206 0 : status = el_retrieve(tag, date, &run, author, type, system, subject,
3207 : text, &size, orig_tag, reply_tag,
3208 : attachment[0], attachment[1], attachment[2], encoding);
3209 :
3210 0 : std::string this_tag = tag;
3211 :
3212 : //printf("js_el_query: el_retrieve: size %d, status %d, tag [%s], run %d, tags [%s] [%s]\n", size, status, tag, run, orig_tag, reply_tag);
3213 :
3214 0 : mstrlcat(tag, "+1", sizeof(tag));
3215 :
3216 : /* check for end run */
3217 0 : if ((r2 > 0) && (r2 < run)) {
3218 0 : break;
3219 : }
3220 :
3221 : /* convert date to unix format */
3222 0 : memset(&tms, 0, sizeof(struct tm));
3223 0 : tms.tm_year = (tag[0] - '0') * 10 + (tag[1] - '0');
3224 0 : tms.tm_mon = (tag[2] - '0') * 10 + (tag[3] - '0') - 1;
3225 0 : tms.tm_mday = (tag[4] - '0') * 10 + (tag[5] - '0');
3226 0 : tms.tm_hour = (date[11] - '0') * 10 + (date[12] - '0');
3227 0 : tms.tm_min = (date[14] - '0') * 10 + (date[15] - '0');
3228 0 : tms.tm_sec = (date[17] - '0') * 10 + (date[18] - '0');
3229 :
3230 0 : if (tms.tm_year < 90)
3231 0 : tms.tm_year += 100;
3232 :
3233 0 : time_t ltime_current = ss_mktime(&tms);
3234 :
3235 : //printf("js_el_query: ltime: start %ld, end %ld, current %ld\n", ltime_start, ltime_end, ltime_current);
3236 :
3237 : /* check for start date */
3238 0 : if (ltime_start > 0)
3239 0 : if (ltime_current < ltime_start)
3240 0 : continue;
3241 :
3242 : /* check for end date */
3243 0 : if (ltime_end > 0) {
3244 0 : if (ltime_current > ltime_end)
3245 0 : break;
3246 : }
3247 :
3248 0 : if (status == EL_SUCCESS) {
3249 : /* do filtering */
3250 0 : if ((ptype.length()>0) && !equal_ustring(ptype.c_str(), type))
3251 0 : continue;
3252 0 : if ((psystem.length()>0) && !equal_ustring(psystem.c_str(), system))
3253 0 : continue;
3254 :
3255 0 : if (pauthor.length()>0) {
3256 0 : mstrlcpy(str, pauthor.c_str(), sizeof(str));
3257 0 : for (i = 0; i < (int) strlen(str); i++)
3258 0 : str[i] = toupper(str[i]);
3259 0 : str[i] = 0;
3260 0 : for (i = 0; i < (int) strlen(author) && author[i] != '@'; i++)
3261 0 : str2[i] = toupper(author[i]);
3262 0 : str2[i] = 0;
3263 :
3264 0 : if (strstr(str2, str) == NULL)
3265 0 : continue;
3266 : }
3267 :
3268 0 : if (psubject.length()>0) {
3269 0 : mstrlcpy(str, psubject.c_str(), sizeof(str));
3270 0 : for (i = 0; i < (int) strlen(str); i++)
3271 0 : str[i] = toupper(str[i]);
3272 0 : str[i] = 0;
3273 0 : for (i = 0; i < (int) strlen(subject); i++)
3274 0 : str2[i] = toupper(subject[i]);
3275 0 : str2[i] = 0;
3276 :
3277 0 : if (strstr(str2, str) == NULL)
3278 0 : continue;
3279 : }
3280 :
3281 0 : if (psubtext.length()>0) {
3282 0 : mstrlcpy(str, psubtext.c_str(), sizeof(str));
3283 0 : for (i = 0; i < (int) strlen(str); i++)
3284 0 : str[i] = toupper(str[i]);
3285 0 : str[i] = 0;
3286 0 : for (i = 0; i < (int) strlen(text); i++)
3287 0 : str2[i] = toupper(text[i]);
3288 0 : str2[i] = 0;
3289 :
3290 0 : if (strstr(str2, str) == NULL)
3291 0 : continue;
3292 : }
3293 :
3294 : /* filter passed: display line */
3295 :
3296 0 : MJsonNode* msg = MJsonNode::MakeObject();
3297 :
3298 0 : ss_repair_utf8(this_tag);
3299 0 : msg->AddToObject("tag", MJsonNode::MakeString(this_tag.c_str()));
3300 0 : ss_repair_utf8(date);
3301 0 : msg->AddToObject("date", MJsonNode::MakeString(date));
3302 0 : msg->AddToObject("run", MJsonNode::MakeInt(run));
3303 0 : ss_repair_utf8(author);
3304 0 : msg->AddToObject("author", MJsonNode::MakeString(author));
3305 0 : ss_repair_utf8(type);
3306 0 : msg->AddToObject("type", MJsonNode::MakeString(type));
3307 0 : ss_repair_utf8(system);
3308 0 : msg->AddToObject("system", MJsonNode::MakeString(system));
3309 0 : ss_repair_utf8(subject);
3310 0 : msg->AddToObject("subject", MJsonNode::MakeString(subject));
3311 0 : ss_repair_utf8(text);
3312 0 : msg->AddToObject("text", MJsonNode::MakeString(text));
3313 0 : ss_repair_utf8(orig_tag);
3314 0 : msg->AddToObject("orig_tag", MJsonNode::MakeString(orig_tag));
3315 0 : ss_repair_utf8(reply_tag);
3316 0 : msg->AddToObject("reply_tag", MJsonNode::MakeString(reply_tag));
3317 0 : ss_repair_utf8(attachment[0]);
3318 0 : msg->AddToObject("attachment0", MJsonNode::MakeString(attachment[0]));
3319 0 : ss_repair_utf8(attachment[1]);
3320 0 : msg->AddToObject("attachment1", MJsonNode::MakeString(attachment[1]));
3321 0 : ss_repair_utf8(attachment[2]);
3322 0 : msg->AddToObject("attachment2", MJsonNode::MakeString(attachment[2]));
3323 0 : ss_repair_utf8(encoding);
3324 0 : msg->AddToObject("encoding", MJsonNode::MakeString(encoding));
3325 :
3326 0 : msg_array->AddToArray(msg);
3327 : }
3328 :
3329 0 : } while (status == EL_SUCCESS);
3330 :
3331 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "msg", msg_array);
3332 0 : }
3333 :
3334 0 : static MJsonNode* js_el_delete(const MJsonNode* params)
3335 : {
3336 0 : if (!params) {
3337 0 : MJSO* doc = MJSO::I();
3338 0 : doc->D("Delete elog message");
3339 0 : doc->P("tag", MJSON_STRING, "tag of message to delete");
3340 0 : doc->R("status", MJSON_INT, "return status of el_delete");
3341 0 : return doc;
3342 : }
3343 :
3344 0 : MJsonNode* error = NULL;
3345 0 : std::string tag = mjsonrpc_get_param(params, "tag", &error)->GetString(); if (error) return error;
3346 0 : int status = el_delete_message(tag.c_str());
3347 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
3348 0 : }
3349 :
3350 :
3351 : /////////////////////////////////////////////////////////////////////////////////
3352 : //
3353 : // jrpc code goes here
3354 : //
3355 : /////////////////////////////////////////////////////////////////////////////////
3356 :
3357 0 : static MJsonNode* jrpc(const MJsonNode* params)
3358 : {
3359 0 : if (!params) {
3360 0 : MJSO* doc = MJSO::I();
3361 0 : doc->D("make RPC call into frontend program via RPC_JRPC");
3362 0 : doc->P("client_name", MJSON_STRING, "Connect to this MIDAS client, see cm_connect_client()");
3363 0 : doc->P("cmd", MJSON_STRING, "Command passed to client");
3364 0 : doc->P("args", MJSON_STRING, "Parameters passed to client as a string, could be JSON encoded");
3365 0 : doc->P("max_reply_length?", MJSON_INT, "Optional maximum length of client reply. MIDAS RPC does not support returning strings of arbitrary length, maximum length has to be known ahead of time.");
3366 0 : doc->R("reply", MJSON_STRING, "Reply from client as a string, could be JSON encoded");
3367 0 : doc->R("status", MJSON_INT, "return status of cm_connect_client() and rpc_client_call()");
3368 0 : return doc;
3369 : }
3370 :
3371 0 : MJsonNode* error = NULL;
3372 :
3373 0 : std::string name = mjsonrpc_get_param(params, "client_name", &error)->GetString(); if (error) return error;
3374 0 : std::string cmd = mjsonrpc_get_param(params, "cmd", &error)->GetString(); if (error) return error;
3375 0 : std::string args = mjsonrpc_get_param(params, "args", &error)->GetString(); if (error) return error;
3376 0 : int max_reply_length = mjsonrpc_get_param(params, "max_reply_length", NULL)->GetInt();
3377 :
3378 : int status;
3379 :
3380 0 : int buf_length = 1024;
3381 :
3382 0 : if (max_reply_length > buf_length)
3383 0 : buf_length = max_reply_length;
3384 :
3385 0 : char* buf = (char*)malloc(buf_length);
3386 :
3387 0 : if (buf == NULL) {
3388 0 : return mjsonrpc_make_error(-32602, "Invalid params", msprintf("malloc(%d) failed, likely invalid max_reply_length value %d", buf_length, max_reply_length).c_str());
3389 : }
3390 :
3391 0 : buf[0] = 0;
3392 :
3393 : HNDLE hconn;
3394 :
3395 0 : status = cm_connect_client(name.c_str(), &hconn);
3396 :
3397 0 : if (status != RPC_SUCCESS) {
3398 0 : free(buf);
3399 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
3400 : }
3401 :
3402 0 : status = rpc_client_call(hconn, RPC_JRPC, cmd.c_str(), args.c_str(), buf, buf_length);
3403 :
3404 : // disconnect return status ignored on purpose.
3405 : // disconnect not needed, there is no limit on number
3406 : // of connections. dead and closed connections are reaped
3407 : // automatically. K.O. Feb 2021.
3408 : // cm_disconnect_client(hconn, FALSE);
3409 :
3410 0 : if (status != RPC_SUCCESS) {
3411 0 : free(buf);
3412 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
3413 : }
3414 :
3415 0 : ss_repair_utf8(buf);
3416 0 : MJsonNode* reply = MJsonNode::MakeString(buf);
3417 0 : free(buf);
3418 :
3419 0 : return mjsonrpc_make_result("reply", reply, "status", MJsonNode::MakeInt(SUCCESS));
3420 0 : }
3421 :
3422 : /////////////////////////////////////////////////////////////////////////////////
3423 : //
3424 : // brpc code goes here
3425 : //
3426 : /////////////////////////////////////////////////////////////////////////////////
3427 :
3428 0 : static MJsonNode* brpc(const MJsonNode* params)
3429 : {
3430 0 : if (!params) {
3431 0 : MJSO* doc = MJSO::I();
3432 0 : doc->D("make RPC call into frontend program via RPC_BRPC");
3433 0 : doc->P("client_name", MJSON_STRING, "Connect to this MIDAS client, see cm_connect_client()");
3434 0 : doc->P("cmd", MJSON_STRING, "Command passed to client");
3435 0 : doc->P("args", MJSON_STRING, "Parameters passed to client as a string, could be JSON encoded");
3436 0 : doc->P("max_reply_length?", MJSON_INT, "Optional maximum length of client reply. MIDAS RPC does not support returning data of arbitrary length, maximum length has to be known ahead of time.");
3437 0 : doc->R("reply", MJSON_STRING, "Reply from client as a string, could be JSON encoded");
3438 0 : doc->R("status", MJSON_INT, "return status of cm_connect_client() and rpc_client_call()");
3439 0 : return doc;
3440 : }
3441 :
3442 0 : MJsonNode* error = NULL;
3443 :
3444 0 : std::string name = mjsonrpc_get_param(params, "client_name", &error)->GetString(); if (error) return error;
3445 0 : std::string cmd = mjsonrpc_get_param(params, "cmd", &error)->GetString(); if (error) return error;
3446 0 : std::string args = mjsonrpc_get_param(params, "args", &error)->GetString(); if (error) return error;
3447 0 : int max_reply_length = mjsonrpc_get_param(params, "max_reply_length", NULL)->GetInt();
3448 :
3449 : int status;
3450 :
3451 0 : int buf_length = 1024;
3452 :
3453 0 : if (max_reply_length > buf_length)
3454 0 : buf_length = max_reply_length;
3455 :
3456 0 : char* buf = (char*)malloc(buf_length);
3457 :
3458 0 : if (buf == NULL) {
3459 0 : return mjsonrpc_make_error(-32602, "Invalid params", msprintf("malloc(%d) failed, likely invalid max_reply_length value %d", buf_length, max_reply_length).c_str());
3460 : }
3461 :
3462 0 : buf[0] = 0;
3463 :
3464 : HNDLE hconn;
3465 :
3466 0 : status = cm_connect_client(name.c_str(), &hconn);
3467 :
3468 0 : if (status != RPC_SUCCESS) {
3469 0 : free(buf);
3470 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
3471 : }
3472 :
3473 0 : status = rpc_client_call(hconn, RPC_BRPC, cmd.c_str(), args.c_str(), buf, &buf_length);
3474 :
3475 : // disconnect return status ignored on purpose.
3476 : // disconnect not needed, there is no limit on number
3477 : // of connections. dead and closed connections are reaped
3478 : // automatically. K.O. Feb 2021.
3479 : // cm_disconnect_client(hconn, FALSE);
3480 :
3481 0 : if (status != RPC_SUCCESS) {
3482 0 : free(buf);
3483 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
3484 : }
3485 :
3486 0 : return MJsonNode::MakeArrayBuffer(buf, buf_length);
3487 0 : }
3488 :
3489 : /////////////////////////////////////////////////////////////////////////////////
3490 : //
3491 : // Run transition code goes here
3492 : //
3493 : /////////////////////////////////////////////////////////////////////////////////
3494 :
3495 0 : static MJsonNode* js_cm_transition(const MJsonNode* params)
3496 : {
3497 0 : if (!params) {
3498 0 : MJSO* doc = MJSO::I();
3499 0 : doc->D("start and stop runs");
3500 0 : doc->P("transition", MJSON_STRING, "requested transition: TR_START, TR_STOP, TR_PAUSE, TR_RESUME");
3501 0 : doc->P("run_number?", MJSON_INT, "New run number, value 0 means /runinfo/run_number + 1, default is 0");
3502 0 : doc->P("async_flag?", MJSON_INT, "Transition type. Default is multithreaded transition TR_MTHREAD");
3503 0 : doc->P("debug_flag?", MJSON_INT, "See cm_transition(), value 1: trace to stdout, value 2: trace to midas.log");
3504 0 : doc->R("status", MJSON_INT, "return status of cm_transition()");
3505 0 : doc->R("error_string?", MJSON_STRING, "return error string from cm_transition()");
3506 0 : return doc;
3507 : }
3508 :
3509 0 : MJsonNode* error = NULL;
3510 :
3511 0 : std::string xtransition = mjsonrpc_get_param(params, "transition", &error)->GetString(); if (error) return error;
3512 0 : int run_number = mjsonrpc_get_param(params, "run_number", NULL)->GetInt();
3513 0 : int async_flag = mjsonrpc_get_param(params, "async_flag", NULL)->GetInt();
3514 0 : int debug_flag = mjsonrpc_get_param(params, "debug_flag", NULL)->GetInt();
3515 :
3516 : int status;
3517 :
3518 0 : int transition = 0;
3519 :
3520 0 : if (xtransition == "TR_START")
3521 0 : transition = TR_START;
3522 0 : else if (xtransition == "TR_STOP")
3523 0 : transition = TR_STOP;
3524 0 : else if (xtransition == "TR_PAUSE")
3525 0 : transition = TR_PAUSE;
3526 0 : else if (xtransition == "TR_RESUME")
3527 0 : transition = TR_RESUME;
3528 : else {
3529 0 : return mjsonrpc_make_error(15, "invalid value of \"transition\"", xtransition.c_str());
3530 : }
3531 :
3532 0 : if (async_flag == 0)
3533 0 : async_flag = TR_MTHREAD;
3534 :
3535 : char error_str[1024];
3536 :
3537 0 : status = cm_transition(transition, run_number, error_str, sizeof(error_str), async_flag, debug_flag);
3538 :
3539 0 : MJsonNode* result = MJsonNode::MakeObject();
3540 :
3541 0 : result->AddToObject("status", MJsonNode::MakeInt(status));
3542 0 : if (strlen(error_str) > 0) {
3543 0 : ss_repair_utf8(error_str);
3544 0 : result->AddToObject("error_string", MJsonNode::MakeString(error_str));
3545 : }
3546 0 : return mjsonrpc_make_result(result);
3547 0 : }
3548 :
3549 : /////////////////////////////////////////////////////////////////////////////////
3550 : //
3551 : // Event buffer code goes here
3552 : //
3553 : /////////////////////////////////////////////////////////////////////////////////
3554 :
3555 0 : static const EVENT_HEADER* CopyEvent(const EVENT_HEADER* pevent)
3556 : {
3557 0 : size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
3558 : //size_t total_size = ALIGN8(event_size);
3559 0 : EVENT_HEADER* ptr = (EVENT_HEADER*)malloc(event_size);
3560 0 : assert(ptr);
3561 0 : memcpy(ptr, pevent, event_size);
3562 0 : return ptr;
3563 : }
3564 :
3565 : struct EventStashEntry
3566 : {
3567 : std::string buffer_name;
3568 : int event_id = 0;
3569 : int trigger_mask = 0;
3570 : const EVENT_HEADER* pevent = NULL;
3571 :
3572 0 : ~EventStashEntry() // dtor
3573 : {
3574 : //Print(); printf(", dtor!\n");
3575 0 : buffer_name.clear();
3576 0 : event_id = 0;
3577 0 : trigger_mask = 0;
3578 0 : if (pevent)
3579 0 : free((void*)pevent);
3580 0 : pevent = NULL;
3581 0 : }
3582 :
3583 0 : void ReplaceEvent(const EVENT_HEADER* xpevent)
3584 : {
3585 0 : if (pevent) {
3586 0 : free((void*)pevent);
3587 0 : pevent = NULL;
3588 : }
3589 0 : pevent = CopyEvent(xpevent);
3590 0 : }
3591 :
3592 : void Print() const
3593 : {
3594 : printf("EventStashEntry: %s,%d,0x%x,%p", buffer_name.c_str(), event_id, trigger_mask, pevent);
3595 : if (pevent)
3596 : printf(", size %d, serial %d, time %d, event_id %d, trigger_mask %x", pevent->data_size, pevent->serial_number, pevent->time_stamp, pevent->event_id, pevent->trigger_mask);
3597 : }
3598 : };
3599 :
3600 : static std::mutex gEventStashMutex;
3601 : static std::vector<EventStashEntry*> gEventStash;
3602 :
3603 0 : static void StashEvent(const std::string buffer_name, int event_id, int trigger_mask, const EVENT_HEADER* pevent)
3604 : {
3605 0 : std::lock_guard<std::mutex> guard(gEventStashMutex);
3606 0 : bool found = false;
3607 0 : for (EventStashEntry* s : gEventStash) {
3608 0 : if (s->buffer_name != buffer_name)
3609 0 : continue;
3610 0 : if (s->event_id == event_id && s->trigger_mask == trigger_mask) {
3611 0 : found = true;
3612 0 : s->ReplaceEvent(pevent);
3613 : //s->Print(); printf(", replaced\n");
3614 0 : } else if (bm_match_event(s->event_id, s->trigger_mask, pevent)) {
3615 0 : s->ReplaceEvent(pevent);
3616 : //s->Print(); printf(", matched\n");
3617 : }
3618 : }
3619 0 : if (!found) {
3620 0 : EventStashEntry* s = new EventStashEntry();
3621 0 : s->buffer_name = buffer_name;
3622 0 : s->event_id = event_id;
3623 0 : s->trigger_mask = trigger_mask;
3624 0 : s->pevent = CopyEvent(pevent);
3625 : //s->Print(); printf(", added\n");
3626 0 : gEventStash.push_back(s);
3627 : }
3628 0 : }
3629 :
3630 0 : static void MatchEvent(const std::string buffer_name, const EVENT_HEADER* pevent)
3631 : {
3632 0 : std::lock_guard<std::mutex> guard(gEventStashMutex);
3633 0 : for (EventStashEntry* s : gEventStash) {
3634 0 : if (s->buffer_name != buffer_name)
3635 0 : continue;
3636 0 : if (bm_match_event(s->event_id, s->trigger_mask, pevent)) {
3637 0 : s->ReplaceEvent(pevent);
3638 : //s->Print(); printf(", matched\n");
3639 : }
3640 : }
3641 0 : }
3642 :
3643 0 : static void DeleteEventStash()
3644 : {
3645 0 : std::lock_guard<std::mutex> guard(gEventStashMutex);
3646 0 : for (size_t i=0; i<gEventStash.size(); i++) {
3647 0 : if (gEventStash[i]) {
3648 0 : delete gEventStash[i];
3649 0 : gEventStash[i] = NULL;
3650 : }
3651 : }
3652 0 : }
3653 :
3654 0 : static const EVENT_HEADER* FindEvent(const std::string buffer_name, int event_id, int trigger_mask, int last_event_id, int last_trigger_mask, DWORD last_serial_number, DWORD last_time_stamp)
3655 : {
3656 0 : std::lock_guard<std::mutex> guard(gEventStashMutex);
3657 0 : for (EventStashEntry* s : gEventStash) {
3658 0 : if (s->buffer_name != buffer_name)
3659 0 : continue;
3660 0 : if (bm_match_event(event_id, trigger_mask, s->pevent)) {
3661 0 : if (s->pevent->event_id == last_event_id
3662 0 : && s->pevent->trigger_mask == last_trigger_mask
3663 0 : && s->pevent->serial_number == last_serial_number
3664 0 : && s->pevent->time_stamp == last_time_stamp) {
3665 : //s->Print(); printf(", already sent for %d,0x%x\n", event_id, trigger_mask);
3666 0 : return NULL;
3667 : } else {
3668 : //s->Print(); printf(", serving for %d,0x%x\n", event_id, trigger_mask);
3669 0 : return CopyEvent(s->pevent);
3670 : }
3671 : }
3672 : }
3673 0 : return NULL;
3674 0 : }
3675 :
3676 0 : static MJsonNode* js_bm_receive_event(const MJsonNode* params)
3677 : {
3678 0 : if (!params) {
3679 0 : MJSO* doc = MJSO::I();
3680 0 : doc->D("read event buffers");
3681 0 : doc->P("buffer_name", MJSON_STRING, "name of event buffer");
3682 0 : doc->P("event_id?", MJSON_INT, "requested event id, -1 means any event id");
3683 0 : doc->P("trigger_mask?", MJSON_INT, "requested trigger mask, -1 means any trigger mask");
3684 0 : doc->P("get_recent?", MJSON_BOOL, "get last available event that matches this event request");
3685 0 : doc->P("last_event_header[]?", MJSON_INT, "do not resend an event we already received: event header of last received event [event_id,trigger_mask,serial_number,time_stamp]");
3686 0 : doc->P("timeout_millisec?", MJSON_NUMBER, "how long to wait for an event");
3687 0 : doc->R("binary data", MJSON_ARRAYBUFFER, "binary event data");
3688 0 : doc->R("status", MJSON_INT, "return status of bm_open_buffer(), bm_request_event(), bm_set_cache_size(), bm_receive_alloc()");
3689 0 : return doc;
3690 : }
3691 :
3692 0 : MJsonNode* error = NULL;
3693 :
3694 0 : std::string buffer_name = mjsonrpc_get_param(params, "buffer_name", &error)->GetString(); if (error) return error;
3695 0 : int event_id = mjsonrpc_get_param(params, "event_id", NULL)->GetInt();
3696 0 : int trigger_mask = mjsonrpc_get_param(params, "trigger_mask", NULL)->GetInt();
3697 0 : bool get_recent = mjsonrpc_get_param(params, "get_recent", NULL)->GetBool();
3698 0 : const MJsonNodeVector* last_event_header = mjsonrpc_get_param(params, "last_event_header", NULL)->GetArray();
3699 0 : int timeout_millisec = mjsonrpc_get_param(params, "timeout_millisec", NULL)->GetInt();
3700 :
3701 0 : int last_event_id = 0;
3702 0 : int last_trigger_mask = 0;
3703 0 : int last_serial_number = 0;
3704 0 : int last_time_stamp = 0;
3705 :
3706 0 : if (last_event_header && last_event_header->size() > 0) {
3707 0 : if (last_event_header->size() != 4) {
3708 0 : return mjsonrpc_make_error(-32602, "Invalid params", "last_event_header should be an array with 4 elements");
3709 : }
3710 :
3711 0 : last_event_id = (*last_event_header)[0]->GetInt();
3712 0 : last_trigger_mask = (*last_event_header)[1]->GetInt();
3713 0 : last_serial_number = (*last_event_header)[2]->GetInt();
3714 0 : last_time_stamp = (*last_event_header)[3]->GetInt();
3715 : }
3716 :
3717 : //printf("last event header: %d %d %d %d\n", last_event_id, last_trigger_mask, last_serial_number, last_time_stamp);
3718 :
3719 0 : if (event_id == 0)
3720 0 : event_id = EVENTID_ALL;
3721 :
3722 0 : if (trigger_mask == 0)
3723 0 : trigger_mask = TRIGGER_ALL;
3724 :
3725 : //printf("js_bm_receive_event: buffer \"%s\", event_id %d, trigger_mask 0x%04x\n", buffer_name.c_str(), event_id, trigger_mask);
3726 :
3727 : int status;
3728 :
3729 0 : HNDLE buffer_handle = 0;
3730 :
3731 0 : status = bm_get_buffer_handle(buffer_name.c_str(), &buffer_handle);
3732 :
3733 0 : if (status != BM_SUCCESS) {
3734 : // if buffer not already open, we need to open it,
3735 : // but we must hold a lock in case multiple RPC handler threads
3736 : // try to open it at the same time. K.O.
3737 : static std::mutex gMutex;
3738 0 : std::lock_guard<std::mutex> lock_guard(gMutex); // lock the mutex
3739 :
3740 : // we have the lock. now we check if some other thread
3741 : // opened the buffer while we were waiting for the lock. K.O.
3742 0 : status = bm_get_buffer_handle(buffer_name.c_str(), &buffer_handle);
3743 :
3744 0 : if (status != BM_SUCCESS) {
3745 0 : status = bm_open_buffer(buffer_name.c_str(), 0, &buffer_handle);
3746 0 : if (status != BM_SUCCESS && status != BM_CREATED) {
3747 0 : MJsonNode* result = MJsonNode::MakeObject();
3748 0 : result->AddToObject("status", MJsonNode::MakeInt(status));
3749 0 : return mjsonrpc_make_result(result);
3750 : }
3751 0 : status = bm_set_cache_size(buffer_handle, 0, 0);
3752 0 : if (status != BM_SUCCESS) {
3753 0 : MJsonNode* result = MJsonNode::MakeObject();
3754 0 : result->AddToObject("status", MJsonNode::MakeInt(status));
3755 0 : return mjsonrpc_make_result(result);
3756 : }
3757 0 : int request_id = 0;
3758 0 : status = bm_request_event(buffer_handle, EVENTID_ALL, TRIGGER_ALL, GET_NONBLOCKING, &request_id, NULL);
3759 0 : if (status != BM_SUCCESS) {
3760 0 : MJsonNode* result = MJsonNode::MakeObject();
3761 0 : result->AddToObject("status", MJsonNode::MakeInt(status));
3762 0 : return mjsonrpc_make_result(result);
3763 : }
3764 : }
3765 0 : }
3766 :
3767 0 : if (timeout_millisec <= 0)
3768 0 : timeout_millisec = 100.0;
3769 :
3770 0 : double start_time = ss_time_sec();
3771 0 : double end_time = start_time + timeout_millisec/1000.0;
3772 :
3773 : // in "GET_RECENT" mode, we read all avialable events from the event buffer
3774 : // and save them in the event stash (MatchEvent()), after we empty the event
3775 : // buffer (BM_ASYNC_RETURN), we send the last saved event. K.O.
3776 :
3777 : while (1) {
3778 0 : EVENT_HEADER* pevent = NULL;
3779 :
3780 0 : status = bm_receive_event_alloc(buffer_handle, &pevent, BM_NO_WAIT);
3781 :
3782 0 : if (status == BM_SUCCESS) {
3783 : //printf("got event_id %d, trigger_mask 0x%04x\n", pevent->event_id, pevent->trigger_mask);
3784 :
3785 0 : if (get_recent) {
3786 0 : if (bm_match_event(event_id, trigger_mask, pevent)) {
3787 0 : StashEvent(buffer_name, event_id, trigger_mask, pevent);
3788 : } else {
3789 0 : MatchEvent(buffer_name, pevent);
3790 : }
3791 0 : free(pevent);
3792 0 : pevent = NULL;
3793 : } else {
3794 0 : if (bm_match_event(event_id, trigger_mask, pevent)) {
3795 0 : StashEvent(buffer_name, event_id, trigger_mask, pevent);
3796 :
3797 0 : size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
3798 : //size_t total_size = ALIGN8(event_size);
3799 0 : return MJsonNode::MakeArrayBuffer((char*)pevent, event_size);
3800 : }
3801 :
3802 0 : MatchEvent(buffer_name, pevent);
3803 :
3804 0 : free(pevent);
3805 0 : pevent = NULL;
3806 : }
3807 0 : } else if (status == BM_ASYNC_RETURN) {
3808 0 : if (get_recent) {
3809 : //printf("bm_async_return!\n");
3810 0 : break;
3811 : }
3812 0 : ss_sleep(10);
3813 : } else {
3814 0 : MJsonNode* result = MJsonNode::MakeObject();
3815 0 : result->AddToObject("status", MJsonNode::MakeInt(status));
3816 0 : return mjsonrpc_make_result(result);
3817 : }
3818 :
3819 0 : if (ss_time_sec() > end_time) {
3820 : //printf("timeout!\n");
3821 0 : break;
3822 : }
3823 0 : }
3824 :
3825 0 : const EVENT_HEADER* pevent = FindEvent(buffer_name, event_id, trigger_mask, last_event_id, last_trigger_mask, last_serial_number, last_time_stamp);
3826 0 : if (pevent) {
3827 0 : size_t event_size = sizeof(EVENT_HEADER) + pevent->data_size;
3828 : //size_t total_size = ALIGN8(event_size);
3829 0 : return MJsonNode::MakeArrayBuffer((char*)pevent, event_size);
3830 : }
3831 :
3832 0 : MJsonNode* result = MJsonNode::MakeObject();
3833 0 : result->AddToObject("status", MJsonNode::MakeInt(BM_ASYNC_RETURN));
3834 0 : return mjsonrpc_make_result(result);
3835 0 : }
3836 :
3837 : /////////////////////////////////////////////////////////////////////////////////
3838 : //
3839 : // ss_system code goes here
3840 : //
3841 : /////////////////////////////////////////////////////////////////////////////////
3842 :
3843 0 : static MJsonNode* js_ss_millitime(const MJsonNode* params)
3844 : {
3845 0 : if (!params) {
3846 0 : MJSO *doc = MJSO::I();
3847 0 : doc->D("get current MIDAS time using ss_millitime()");
3848 0 : doc->P(NULL, 0, "there are no input parameters");
3849 0 : doc->R(NULL, MJSON_INT, "current value of ss_millitime()");
3850 0 : return doc;
3851 : }
3852 :
3853 0 : return mjsonrpc_make_result(MJsonNode::MakeNumber(ss_millitime()));
3854 : }
3855 :
3856 : /////////////////////////////////////////////////////////////////////////////////
3857 : //
3858 : // Alarm code goes here
3859 : //
3860 : /////////////////////////////////////////////////////////////////////////////////
3861 :
3862 0 : static MJsonNode* get_alarms(const MJsonNode* params)
3863 : {
3864 0 : if (!params) {
3865 0 : MJSO* doc = MJSO::I();
3866 0 : doc->D("get alarm data");
3867 0 : doc->P("get_all?", MJSON_BOOL, "get all alarms, even in alarm system not active and alarms not triggered");
3868 0 : doc->R("status", MJSON_INT, "return status of midas library calls");
3869 0 : doc->R("alarm_system_active", MJSON_BOOL, "value of ODB \"/Alarms/alarm system active\"");
3870 0 : doc->R("alarms", MJSON_OBJECT, "alarm data, keyed by alarm name");
3871 0 : doc->R("alarms[].triggered", MJSON_BOOL, "alarm is triggered");
3872 0 : doc->R("alarms[].active", MJSON_BOOL, "alarm is enabled");
3873 0 : doc->R("alarms[].class", MJSON_STRING, "alarm class");
3874 0 : doc->R("alarms[].type", MJSON_INT, "alarm type AT_xxx");
3875 0 : doc->R("alarms[].bgcolor", MJSON_STRING, "display background color");
3876 0 : doc->R("alarms[].fgcolor", MJSON_STRING, "display foreground color");
3877 0 : doc->R("alarms[].message", MJSON_STRING, "alarm ODB message field");
3878 0 : doc->R("alarms[].condition", MJSON_STRING, "alarm ODB condition field");
3879 0 : doc->R("alarms[].evaluated_value?", MJSON_STRING, "evaluated alarm condition (AT_EVALUATED alarms only)");
3880 0 : doc->R("alarms[].periodic_next_time?", MJSON_STRING, "next time the periodic alarm will fire (AT_PERIODIC alarms only)");
3881 0 : doc->R("alarms[].time_triggered_first", MJSON_STRING, "time when alarm was triggered");
3882 0 : doc->R("alarms[].show_to_user", MJSON_STRING, "final alarm text shown to user by mhttpd");
3883 0 : return doc;
3884 : }
3885 :
3886 : //MJsonNode* error = NULL;
3887 :
3888 0 : bool get_all = mjsonrpc_get_param(params, "get_all", NULL)->GetBool();
3889 :
3890 : int status;
3891 : HNDLE hDB;
3892 :
3893 0 : status = cm_get_experiment_database(&hDB, NULL);
3894 :
3895 0 : if (status != DB_SUCCESS) {
3896 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
3897 : }
3898 :
3899 : int flag;
3900 : int size;
3901 0 : int alarm_system_active = 0;
3902 :
3903 : /* check global alarm flag */
3904 0 : flag = TRUE;
3905 0 : size = sizeof(flag);
3906 0 : status = db_get_value(hDB, 0, "/Alarms/Alarm System active", &flag, &size, TID_BOOL, TRUE);
3907 :
3908 0 : if (status != DB_SUCCESS) {
3909 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
3910 : }
3911 :
3912 0 : alarm_system_active = flag;
3913 :
3914 0 : if (!alarm_system_active)
3915 0 : if (!get_all) {
3916 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(SUCCESS),
3917 : "alarm_system_active", MJsonNode::MakeBool(alarm_system_active!=0),
3918 0 : "alarms", MJsonNode::MakeObject());
3919 : }
3920 :
3921 : /* go through all alarms */
3922 : HNDLE hkey;
3923 0 : status = db_find_key(hDB, 0, "/Alarms/Alarms", &hkey);
3924 :
3925 0 : if (status != DB_SUCCESS) {
3926 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
3927 : }
3928 :
3929 0 : MJsonNode* alarms = MJsonNode::MakeObject();
3930 :
3931 0 : for (int i = 0;; i++) {
3932 : HNDLE hsubkey;
3933 : KEY key;
3934 :
3935 0 : db_enum_link(hDB, hkey, i, &hsubkey);
3936 :
3937 0 : if (!hsubkey)
3938 0 : break;
3939 :
3940 0 : status = db_get_key(hDB, hsubkey, &key);
3941 :
3942 0 : const char* name = key.name;
3943 :
3944 0 : flag = 0;
3945 0 : size = sizeof(flag);
3946 0 : status = db_get_value(hDB, hsubkey, "Triggered", &flag, &size, TID_INT, TRUE);
3947 :
3948 : // skip un-triggered alarms
3949 0 : if (!flag)
3950 0 : if (!get_all)
3951 0 : continue;
3952 :
3953 0 : MJsonNode* a = MJsonNode::MakeObject();
3954 :
3955 0 : a->AddToObject("triggered", MJsonNode::MakeBool(flag!=0));
3956 :
3957 0 : flag = 1;
3958 0 : size = sizeof(BOOL);
3959 0 : status = db_get_value(hDB, hsubkey, "Active", &flag, &size, TID_BOOL, TRUE);
3960 :
3961 0 : a->AddToObject("active", MJsonNode::MakeBool(flag!=0));
3962 :
3963 : char alarm_class[NAME_LENGTH];
3964 0 : strcpy(alarm_class, "Alarm");
3965 0 : size = sizeof(alarm_class);
3966 0 : status = db_get_value(hDB, hsubkey, "Alarm Class", alarm_class, &size, TID_STRING, TRUE);
3967 :
3968 0 : ss_repair_utf8(alarm_class);
3969 0 : a->AddToObject("class", MJsonNode::MakeString(alarm_class));
3970 :
3971 0 : int atype = 0;
3972 0 : size = sizeof(atype);
3973 0 : status = db_get_value(hDB, hsubkey, "Type", &atype, &size, TID_INT, TRUE);
3974 :
3975 0 : a->AddToObject("type", MJsonNode::MakeInt(atype));
3976 :
3977 : char str[256];
3978 :
3979 : char bgcol[256];
3980 0 : strcpy(bgcol, "red");
3981 :
3982 0 : if (strlen(alarm_class) > 0) {
3983 0 : sprintf(str, "/Alarms/Classes/%s/Display BGColor", alarm_class);
3984 0 : size = sizeof(bgcol);
3985 0 : status = db_get_value(hDB, 0, str, bgcol, &size, TID_STRING, TRUE);
3986 : }
3987 :
3988 0 : ss_repair_utf8(bgcol);
3989 0 : a->AddToObject("bgcolor", MJsonNode::MakeString(bgcol));
3990 :
3991 : char fgcol[256];
3992 0 : strcpy(fgcol, "black");
3993 :
3994 0 : if (strlen(alarm_class) > 0) {
3995 0 : sprintf(str, "/Alarms/Classes/%s/Display FGColor", alarm_class);
3996 0 : size = sizeof(fgcol);
3997 0 : status = db_get_value(hDB, 0, str, fgcol, &size, TID_STRING, TRUE);
3998 : }
3999 :
4000 0 : ss_repair_utf8(fgcol);
4001 0 : a->AddToObject("fgcolor", MJsonNode::MakeString(fgcol));
4002 :
4003 0 : flag = 1;
4004 0 : if (strlen(alarm_class) > 0) {
4005 0 : size = sizeof(BOOL);
4006 0 : sprintf(str, "/Alarms/Classes/%s/Alarm sound", alarm_class);
4007 0 : status = db_get_value(hDB, 0, str, &flag, &size, TID_BOOL, TRUE);
4008 : }
4009 0 : a->AddToObject("alarm_sound", MJsonNode::MakeBool(flag!=0));
4010 :
4011 : char msg[256];
4012 0 : msg[0] = 0;
4013 0 : size = sizeof(msg);
4014 0 : status = db_get_value(hDB, hsubkey, "Alarm Message", msg, &size, TID_STRING, TRUE);
4015 :
4016 0 : ss_repair_utf8(msg);
4017 0 : a->AddToObject("message", MJsonNode::MakeString(msg));
4018 :
4019 : char cond[256];
4020 0 : cond[0] = 0;
4021 0 : size = sizeof(cond);
4022 0 : status = db_get_value(hDB, hsubkey, "Condition", cond, &size, TID_STRING, TRUE);
4023 :
4024 0 : ss_repair_utf8(cond);
4025 0 : a->AddToObject("condition", MJsonNode::MakeString(cond));
4026 :
4027 0 : std::string show_to_user;
4028 :
4029 0 : if (atype == AT_EVALUATED) {
4030 : /* retrieve value */
4031 0 : std::string value;
4032 0 : al_evaluate_condition(name, cond, &value);
4033 0 : show_to_user = msprintf(msg, value.c_str());
4034 0 : ss_repair_utf8(value);
4035 0 : a->AddToObject("evaluated_value", MJsonNode::MakeString(value.c_str()));
4036 0 : } else
4037 0 : show_to_user = msg;
4038 :
4039 0 : ss_repair_utf8(show_to_user);
4040 0 : a->AddToObject("show_to_user", MJsonNode::MakeString(show_to_user.c_str()));
4041 :
4042 0 : str[0] = 0;
4043 0 : size = sizeof(str);
4044 0 : status = db_get_value(hDB, hsubkey, "Time triggered first", str, &size, TID_STRING, TRUE);
4045 :
4046 0 : ss_repair_utf8(str);
4047 0 : a->AddToObject("time_triggered_first", MJsonNode::MakeString(str));
4048 :
4049 0 : if (atype == AT_PERIODIC) {
4050 0 : DWORD last = 0;
4051 0 : size = sizeof(last);
4052 0 : db_get_value(hDB, hsubkey, "Checked last", &last, &size, TID_DWORD, TRUE);
4053 :
4054 0 : if (last == 0) {
4055 0 : last = ss_time();
4056 0 : db_set_value(hDB, hsubkey, "Checked last", &last, size, 1, TID_DWORD);
4057 : }
4058 :
4059 0 : int interval = 0;
4060 0 : size = sizeof(interval);
4061 0 : db_get_value(hDB, hsubkey, "Check interval", &interval, &size, TID_INT, TRUE);
4062 :
4063 0 : time_t tnext = last + interval;
4064 :
4065 : char ctimebuf[32];
4066 0 : ctime_r(&tnext, ctimebuf);
4067 :
4068 : //ss_repair_utf8(ctimebuf); redundant!
4069 0 : a->AddToObject("periodic_next_time", MJsonNode::MakeString(ctimebuf));
4070 : }
4071 :
4072 0 : alarms->AddToObject(name, a);
4073 0 : }
4074 :
4075 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(SUCCESS),
4076 : "alarm_system_active", MJsonNode::MakeBool(alarm_system_active!=0),
4077 0 : "alarms", alarms);
4078 : }
4079 :
4080 : /////////////////////////////////////////////////////////////////////////////////
4081 : //
4082 : // Sequencer and file_picker code goes here
4083 : //
4084 : /////////////////////////////////////////////////////////////////////////////////
4085 :
4086 0 : static MJsonNode* js_make_subdir(const MJsonNode* params)
4087 : {
4088 0 : if (!params) {
4089 0 : MJSO* doc = MJSO::I();
4090 0 : doc->D("js_make_subdir");
4091 0 : doc->P("subdir", MJSON_STRING, "Create folder experiment_directory/userfiles/subdir");
4092 0 : doc->R("status", MJSON_INT, "return status of midas library calls");
4093 0 : doc->R("path", MJSON_STRING, "Search path");
4094 0 : return doc;
4095 : }
4096 :
4097 0 : MJsonNode* error = NULL;
4098 :
4099 0 : std::string subdir = mjsonrpc_get_param(params, "subdir", &error)->GetString(); if (error) return error;
4100 :
4101 : /*---- Not allowed to contain ../ - safety feature ----*/
4102 0 : if (subdir.find("..") != std::string::npos) {
4103 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The subdir is not permitted"));
4104 : }
4105 :
4106 : int status;
4107 : HNDLE hDB;
4108 :
4109 0 : status = cm_get_experiment_database(&hDB, NULL);
4110 :
4111 0 : if (status != DB_SUCCESS) {
4112 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
4113 : }
4114 :
4115 0 : std::string path = cm_expand_env(cm_get_path().c_str());
4116 0 : if (path[path.length()-1] != DIR_SEPARATOR) {
4117 0 : path += DIR_SEPARATOR_STR;
4118 : }
4119 0 : path += "userfiles";
4120 :
4121 : // Check if the userfiles folder exists
4122 0 : if (access(path.c_str(), F_OK) != 0) {
4123 : // Create the path if it doesn't exist
4124 0 : if (mkdir(path.c_str(), 0777) != 0) {
4125 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
4126 : }
4127 : }
4128 :
4129 : // Add subdir to userfiles
4130 0 : if (subdir.length() > 0) {
4131 0 : if (subdir[0] != DIR_SEPARATOR) {
4132 0 : path += DIR_SEPARATOR_STR;
4133 : }
4134 0 : path += subdir;
4135 : }
4136 : // Check if the subdir exisits in userfiles, otherwise create it
4137 0 : if (access(path.c_str(), F_OK) != 0) {
4138 : // Create the path if it doesn't exist
4139 0 : if (mkdir(path.c_str(), 0777) != 0) {
4140 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create subdirectory"));
4141 : }
4142 : }
4143 :
4144 :
4145 0 : MJsonNode* r = MJsonNode::MakeObject();
4146 0 : r->AddToObject("status", MJsonNode::MakeInt(SUCCESS));
4147 0 : ss_repair_utf8(path);
4148 0 : r->AddToObject("path", MJsonNode::MakeString(path.c_str()));
4149 0 : return mjsonrpc_make_result(r);
4150 0 : }
4151 :
4152 0 : static MJsonNode* js_ext_list_files(const MJsonNode* params)
4153 : {
4154 0 : if (!params) {
4155 0 : MJSO* doc = MJSO::I();
4156 0 : doc->D("js_ext_list_files");
4157 0 : doc->P("subdir", MJSON_STRING, "List files in experiment_directory/userfiles/subdir");
4158 0 : doc->P("fileext", MJSON_STRING, "Filename extension");
4159 0 : doc->R("status", MJSON_INT, "return status of midas library calls");
4160 0 : doc->R("path", MJSON_STRING, "Search path");
4161 0 : doc->R("subdirs[]", MJSON_STRING, "list of subdirectories");
4162 0 : doc->R("files[].filename", MJSON_STRING, "script filename");
4163 0 : doc->R("files[].description", MJSON_STRING, "script description");
4164 0 : return doc;
4165 : }
4166 :
4167 0 : MJsonNode* error = NULL;
4168 0 : std::string subdir = mjsonrpc_get_param(params, "subdir", &error)->GetString(); if (error) return error;
4169 0 : std::string fileext = mjsonrpc_get_param(params, "fileext", &error)->GetString(); if (error) return error;
4170 :
4171 : /*---- Not allowed to contain ../ - safety feature ----*/
4172 0 : if (subdir.find("..") != std::string::npos) {
4173 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The subdir is not permitted"));
4174 : }
4175 :
4176 : /*---- ext must start with *. and some not allowed ext - safety feature */
4177 0 : if (fileext.find("..") != std::string::npos) {
4178 : /*
4179 : fileext.find("/") != std::string::npos ||
4180 : fileext.find("*.") != 0 ||
4181 : fileext.find("*.html") != std::string::npos || fileext.find("*.HTML") != std::string::npos ||
4182 : fileext.find("*.htm") != std::string::npos || fileext.find("*.HTM") != std::string::npos ||
4183 : fileext.find("*.js") != std::string::npos || fileext.find("*.JS") != std::string::npos ||
4184 : fileext.find("*.pl") != std::string::npos || fileext.find("*.PL") != std::string::npos ||
4185 : fileext.find("*.cgi") != std::string::npos || fileext.find("*.CGI") != std::string::npos ||
4186 : fileext.find("*.*") != std::string::npos
4187 : ) {
4188 : */
4189 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The filename extension is not permitted"));
4190 : }
4191 :
4192 : int status;
4193 : HNDLE hDB;
4194 :
4195 0 : status = cm_get_experiment_database(&hDB, NULL);
4196 :
4197 0 : if (status != DB_SUCCESS) {
4198 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status));
4199 : }
4200 :
4201 0 : std::string path = cm_expand_env(cm_get_path().c_str());
4202 0 : if (path[path.length()-1] != DIR_SEPARATOR) {
4203 0 : path += DIR_SEPARATOR_STR;
4204 : }
4205 0 : path += "userfiles";
4206 :
4207 : // Check if the userfiles folder exists
4208 0 : if (access(path.c_str(), F_OK) != 0) {
4209 : // Create the path if it doesn't exist
4210 0 : if (mkdir(path.c_str(), 0777) != 0) {
4211 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
4212 : }
4213 : }
4214 :
4215 0 : if (subdir.length() > 0) {
4216 0 : if (subdir[0] != DIR_SEPARATOR) {
4217 0 : path += DIR_SEPARATOR_STR;
4218 : }
4219 0 : path += subdir;
4220 : }
4221 :
4222 0 : char* flist = NULL;
4223 0 : MJsonNode* s = MJsonNode::MakeArray();
4224 :
4225 : /*---- go over subdirectories ----*/
4226 0 : int n = ss_dirlink_find(path.c_str(), "*", &flist);
4227 :
4228 0 : for (int i=0 ; i<n ; i++) {
4229 0 : if (flist[i*MAX_STRING_LENGTH] != '.') {
4230 : //printf("subdir %d: [%s]\n", i, flist+i*MAX_STRING_LENGTH);
4231 0 : ss_repair_utf8(flist+i*MAX_STRING_LENGTH);
4232 0 : s->AddToArray(MJsonNode::MakeString(flist+i*MAX_STRING_LENGTH));
4233 : }
4234 : }
4235 :
4236 0 : MJsonNode* f = MJsonNode::MakeArray();
4237 0 : time_t modtime = time(NULL);
4238 : double fsize;
4239 : /*---- go over files with extension fileext in path ----*/
4240 0 : n = ss_file_find(path.c_str(), fileext.c_str(), &flist);
4241 :
4242 0 : if (n == -1) {
4243 : // path does not exist, return an error
4244 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Subdirectory does not exist, create it first."));
4245 : }
4246 :
4247 0 : for (int i=0 ; i<n ; i++) {
4248 : //printf("file %d: [%s]\n", i, flist+i*MAX_STRING_LENGTH);
4249 0 : MJsonNode* o = MJsonNode::MakeObject();
4250 0 : ss_repair_utf8(flist+i*MAX_STRING_LENGTH);
4251 0 : o->AddToObject("filename", MJsonNode::MakeString(flist+i*MAX_STRING_LENGTH));
4252 : /* description is msl specific, do we need it? */
4253 0 : std::string full_name = path;
4254 0 : if (full_name.back() != DIR_SEPARATOR)
4255 0 : full_name += DIR_SEPARATOR;
4256 0 : full_name.append(flist+i*MAX_STRING_LENGTH);
4257 : // o->AddToObject("description", MJsonNode::MakeString(full_name.c_str()));
4258 0 : o->AddToObject("description", MJsonNode::MakeString("description"));
4259 0 : modtime = ss_file_time(full_name.c_str());
4260 0 : o->AddToObject("modtime", MJsonNode::MakeInt(modtime));
4261 0 : fsize = ss_file_size(full_name.c_str());
4262 0 : o->AddToObject("size", MJsonNode::MakeInt(fsize));
4263 0 : f->AddToArray(o);
4264 0 : }
4265 :
4266 0 : free(flist);
4267 0 : flist = NULL;
4268 :
4269 0 : MJsonNode* r = MJsonNode::MakeObject();
4270 0 : r->AddToObject("status", MJsonNode::MakeInt(SUCCESS));
4271 0 : ss_repair_utf8(path);
4272 0 : r->AddToObject("path", MJsonNode::MakeString(path.c_str()));
4273 0 : r->AddToObject("subdirs", s);
4274 0 : r->AddToObject("files", f);
4275 :
4276 0 : return mjsonrpc_make_result(r);
4277 :
4278 0 : }
4279 :
4280 0 : static MJsonNode* js_ext_save_file(const MJsonNode* params)
4281 : {
4282 0 : if (!params) {
4283 0 : MJSO* doc = MJSO::I();
4284 0 : doc->D("js_ext_save_file");
4285 0 : doc->P("filename", MJSON_STRING, "File name, save in experiment_directory/userfiles/filename");
4286 0 : doc->P("script", MJSON_STRING, "ASCII content");
4287 0 : doc->R("status", MJSON_INT, "return status of midas library calls");
4288 0 : doc->R("error", MJSON_STRING, "error text");
4289 0 : return doc;
4290 : }
4291 :
4292 : /* Need to make sure that content cannot be abused (e.g. not Javascript code), how?? */
4293 :
4294 0 : MJsonNode* error = NULL;
4295 0 : std::string filename = mjsonrpc_get_param(params, "filename", &error)->GetString(); if (error) return error;
4296 0 : std::string script = mjsonrpc_get_param(params, "script", &error)->GetString(); if (error) return error;
4297 :
4298 : /*---- filename should not contain the following - safety feature */
4299 0 : if (filename.find("..") != std::string::npos ) {
4300 : /*
4301 : filename.find("*") != std::string::npos ||
4302 : filename.find(".html") != std::string::npos || filename.find(".HTML") != std::string::npos ||
4303 : filename.find(".htm") != std::string::npos || filename.find(".HTM") != std::string::npos ||
4304 : filename.find(".js") != std::string::npos || filename.find(".JS") != std::string::npos ||
4305 : filename.find(".pl") != std::string::npos || filename.find(".PL") != std::string::npos ||
4306 : filename.find(".cgi") != std::string::npos || filename.find(".CGI") != std::string::npos
4307 : ) {
4308 : */
4309 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The filename is not permitted"));
4310 : }
4311 :
4312 : int status;
4313 : HNDLE hDB;
4314 :
4315 0 : status = cm_get_experiment_database(&hDB, NULL);
4316 :
4317 0 : if (status != DB_SUCCESS) {
4318 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString("cm_get_experiment_database() error"));
4319 : }
4320 :
4321 0 : std::string path = cm_expand_env(cm_get_path().c_str());
4322 0 : path += "userfiles";
4323 :
4324 : // Check if the userfiles folder exists
4325 0 : if (access(path.c_str(), F_OK) != 0) {
4326 : // Create the path if it doesn't exist
4327 0 : if (mkdir(path.c_str(), 0777) != 0) {
4328 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
4329 : }
4330 : }
4331 :
4332 0 : path += DIR_SEPARATOR_STR;
4333 0 : path += filename;
4334 :
4335 0 : FILE* fp = fopen(path.c_str(), "w");
4336 0 : if (!fp) {
4337 0 : status = SS_FILE_ERROR;
4338 : char errstr[256];
4339 0 : sprintf(errstr, "fopen() errno %d (%s)", errno, strerror(errno));
4340 0 : ss_repair_utf8(errstr);
4341 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr));
4342 : }
4343 :
4344 0 : fwrite(script.c_str(), script.length(), 1, fp);
4345 : //fprintf(fp, "\n");
4346 0 : fclose(fp);
4347 0 : fp = NULL;
4348 :
4349 0 : status = CM_SUCCESS;
4350 0 : std::string errstr = "no error";
4351 :
4352 : //ss_repair_utf8(errstr); redundant!
4353 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr.c_str()));
4354 0 : }
4355 :
4356 0 : static MJsonNode* js_ext_read_file(const MJsonNode* params)
4357 : {
4358 0 : if (!params) {
4359 0 : MJSO* doc = MJSO::I();
4360 0 : doc->D("js_ext_read_script");
4361 0 : doc->P("filename", MJSON_STRING, "File name, read from experiment_directory/userfiles/filename");
4362 0 : doc->R("content", MJSON_STRING, "ASCII file content");
4363 0 : doc->R("status", MJSON_INT, "return status of midas library calls");
4364 0 : doc->R("error", MJSON_STRING, "error text");
4365 0 : return doc;
4366 : }
4367 :
4368 0 : MJsonNode* error = NULL;
4369 0 : std::string filename = mjsonrpc_get_param(params, "filename", &error)->GetString(); if (error) return error;
4370 :
4371 : /*---- filename should not contain the following - safety feature */
4372 0 : if (filename.find("..") != std::string::npos ||
4373 0 : filename.find("*") != std::string::npos) {
4374 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The filename is not permitted"));
4375 : }
4376 :
4377 : int status;
4378 : HNDLE hDB;
4379 :
4380 0 : status = cm_get_experiment_database(&hDB, NULL);
4381 :
4382 0 : if (status != DB_SUCCESS) {
4383 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString("cm_get_experiment_database() error"));
4384 : }
4385 :
4386 0 : std::string path = cm_expand_env(cm_get_path().c_str());
4387 0 : if (path[path.length()-1] != DIR_SEPARATOR) {
4388 0 : path += DIR_SEPARATOR_STR;
4389 : }
4390 0 : path += "userfiles";
4391 :
4392 : // Check if the userfiles folder exists
4393 0 : if (access(path.c_str(), F_OK) != 0) {
4394 : // Create the path if it doesn't exist
4395 0 : if (mkdir(path.c_str(), 0777) != 0) {
4396 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
4397 : }
4398 : }
4399 :
4400 0 : path += DIR_SEPARATOR_STR;
4401 0 : path += filename;
4402 :
4403 0 : FILE* fp = fopen(path.c_str(), "r");
4404 0 : if (!fp) {
4405 0 : status = SS_FILE_ERROR;
4406 : char errstr[256];
4407 0 : sprintf(errstr, "fopen() errno %d (%s)", errno, strerror(errno));
4408 0 : ss_repair_utf8(errstr);
4409 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr));
4410 : }
4411 :
4412 0 : fseek(fp, 0, SEEK_END);
4413 0 : size_t file_size = ftell(fp);
4414 0 : rewind(fp);
4415 :
4416 0 : char* buffer = new char[file_size+1];
4417 0 : fread(buffer, file_size, 1, fp);
4418 : // Maybe not needed here
4419 0 : buffer[file_size] = '\0';
4420 0 : fclose(fp);
4421 :
4422 0 : std::string content = buffer;
4423 0 : delete[] buffer;
4424 0 : buffer = NULL;
4425 :
4426 0 : status = CM_SUCCESS;
4427 0 : std::string errstr = "no error";
4428 :
4429 0 : return mjsonrpc_make_result("content", MJsonNode::MakeString(content.c_str()), "status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr.c_str()));
4430 0 : }
4431 :
4432 : /////////////////////////////////////////////////////////////////////////////////
4433 : //
4434 : // Binary files reading for file_picker code goes here
4435 : //
4436 : /////////////////////////////////////////////////////////////////////////////////
4437 :
4438 0 : static MJsonNode* js_read_binary_file(const MJsonNode* params) {
4439 0 : if (!params) {
4440 0 : MJSO* doc = MJSO::I();
4441 0 : doc->D("js_read_binary_file");
4442 0 : doc->P("filename", MJSON_STRING, "File name, read from experiment_directory/userfiles/filename");
4443 0 : doc->R("binary data", MJSON_ARRAYBUFFER, "Binary file content");
4444 0 : doc->R("status", MJSON_INT, "Return status of midas library calls");
4445 0 : doc->R("error", MJSON_STRING, "Error text");
4446 0 : return doc;
4447 : }
4448 :
4449 0 : MJsonNode* error = NULL;
4450 0 : std::string filename = mjsonrpc_get_param(params, "filename", &error)->GetString();
4451 0 : if (error) return error;
4452 :
4453 : /*---- filename should not contain the following - safety feature */
4454 0 : if (filename.find("..") != std::string::npos ||
4455 0 : filename.find("*") != std::string::npos) {
4456 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("The filename is not permitted"));
4457 : }
4458 :
4459 : int status;
4460 : HNDLE hDB;
4461 :
4462 0 : status = cm_get_experiment_database(&hDB, NULL);
4463 :
4464 0 : if (status != DB_SUCCESS) {
4465 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString("cm_get_experiment_database() error"));
4466 : }
4467 :
4468 0 : std::string path = cm_expand_env(cm_get_path().c_str());
4469 0 : if (path[path.length()-1] != DIR_SEPARATOR) {
4470 0 : path += DIR_SEPARATOR_STR;
4471 : }
4472 0 : path += "userfiles";
4473 :
4474 : // Check if the userfiles folder exists
4475 0 : if (access(path.c_str(), F_OK) != 0) {
4476 : // Create the path if it doesn't exist
4477 0 : if (mkdir(path.c_str(), 0777) != 0) {
4478 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(DB_INVALID_PARAM), "error", MJsonNode::MakeString("Failed to create the userfiles folder"));
4479 : }
4480 : }
4481 :
4482 0 : path += DIR_SEPARATOR_STR;
4483 0 : path += filename;
4484 :
4485 0 : FILE* fp = fopen(path.c_str(), "rb");
4486 0 : if (!fp) {
4487 0 : status = SS_FILE_ERROR;
4488 : char errstr[256];
4489 0 : sprintf(errstr, "fopen() errno %d (%s)", errno, strerror(errno));
4490 0 : ss_repair_utf8(errstr);
4491 0 : return mjsonrpc_make_result("status", MJsonNode::MakeInt(status), "error", MJsonNode::MakeString(errstr));
4492 : }
4493 :
4494 0 : fseek(fp, 0, SEEK_END);
4495 0 : size_t file_size = ftell(fp);
4496 0 : rewind(fp);
4497 :
4498 0 : char* buffer = new char[file_size];
4499 0 : fread(buffer, file_size, 1, fp);
4500 : // maybe not needed here
4501 : //buffer[file_size] = '\0';
4502 0 : fclose(fp);
4503 :
4504 0 : status = CM_SUCCESS;
4505 0 : std::string errstr = "no error";
4506 :
4507 0 : MJsonNode* result = MJsonNode::MakeArrayBuffer(buffer, file_size);
4508 0 : return result;
4509 0 : }
4510 :
4511 :
4512 : /////////////////////////////////////////////////////////////////////////////////
4513 : //
4514 : // JSON-RPC management code goes here
4515 : //
4516 : /////////////////////////////////////////////////////////////////////////////////
4517 :
4518 0 : static MJsonNode* get_debug(const MJsonNode* params)
4519 : {
4520 0 : if (!params) {
4521 0 : MJSO *doc = MJSO::I();
4522 0 : doc->D("get current value of mjsonrpc_debug");
4523 0 : doc->P(NULL, 0, "there are no input parameters");
4524 0 : doc->R(NULL, MJSON_INT, "current value of mjsonrpc_debug");
4525 0 : return doc;
4526 : }
4527 :
4528 0 : return mjsonrpc_make_result("debug", MJsonNode::MakeInt(mjsonrpc_debug));
4529 : }
4530 :
4531 0 : static MJsonNode* set_debug(const MJsonNode* params)
4532 : {
4533 0 : if (!params) {
4534 0 : MJSO* doc = MJSO::I();
4535 0 : doc->D("set new value of mjsonrpc_debug");
4536 0 : doc->P(NULL, MJSON_INT, "new value of mjsonrpc_debug");
4537 0 : doc->R(NULL, MJSON_INT, "new value of mjsonrpc_debug");
4538 0 : return doc;
4539 : }
4540 :
4541 0 : mjsonrpc_debug = params->GetInt();
4542 0 : return mjsonrpc_make_result("debug", MJsonNode::MakeInt(mjsonrpc_debug));
4543 : }
4544 :
4545 0 : static MJsonNode* get_sleep(const MJsonNode* params)
4546 : {
4547 0 : if (!params) {
4548 0 : MJSO *doc = MJSO::I();
4549 0 : doc->D("get current value of mjsonrpc_sleep");
4550 0 : doc->P(NULL, 0, "there are no input parameters");
4551 0 : doc->R(NULL, MJSON_INT, "current value of mjsonrpc_sleep");
4552 0 : return doc;
4553 : }
4554 :
4555 0 : return mjsonrpc_make_result("sleep", MJsonNode::MakeInt(mjsonrpc_sleep));
4556 : }
4557 :
4558 0 : static MJsonNode* set_sleep(const MJsonNode* params)
4559 : {
4560 0 : if (!params) {
4561 0 : MJSO* doc = MJSO::I();
4562 0 : doc->D("set new value of mjsonrpc_sleep");
4563 0 : doc->P(NULL, MJSON_INT, "new value of mjsonrpc_sleep");
4564 0 : doc->R(NULL, MJSON_INT, "new value of mjsonrpc_sleep");
4565 0 : return doc;
4566 : }
4567 :
4568 0 : mjsonrpc_sleep = params->GetInt();
4569 0 : return mjsonrpc_make_result("sleep", MJsonNode::MakeInt(mjsonrpc_sleep));
4570 : }
4571 :
4572 0 : static MJsonNode* get_time(const MJsonNode* params)
4573 : {
4574 0 : if (!params) {
4575 0 : MJSO *doc = MJSO::I();
4576 0 : doc->D("get current value of mjsonrpc_time");
4577 0 : doc->P(NULL, 0, "there are no input parameters");
4578 0 : doc->R(NULL, MJSON_INT, "current value of mjsonrpc_time");
4579 0 : return doc;
4580 : }
4581 :
4582 0 : return mjsonrpc_make_result("time", MJsonNode::MakeInt(mjsonrpc_time));
4583 : }
4584 :
4585 0 : static MJsonNode* set_time(const MJsonNode* params)
4586 : {
4587 0 : if (!params) {
4588 0 : MJSO* doc = MJSO::I();
4589 0 : doc->D("set new value of mjsonrpc_time");
4590 0 : doc->P(NULL, MJSON_INT, "new value of mjsonrpc_time");
4591 0 : doc->R(NULL, MJSON_INT, "new value of mjsonrpc_time");
4592 0 : return doc;
4593 : }
4594 :
4595 0 : mjsonrpc_time = params->GetInt();
4596 0 : return mjsonrpc_make_result("time", MJsonNode::MakeInt(mjsonrpc_time));
4597 : }
4598 :
4599 0 : static MJsonNode* get_schema(const MJsonNode* params)
4600 : {
4601 0 : if (!params) {
4602 0 : MJSO* doc = MJSO::I();
4603 0 : doc->D("Get the MIDAS JSON-RPC schema JSON object");
4604 0 : doc->P(NULL, 0, "there are no input parameters");
4605 0 : doc->R(NULL, MJSON_OBJECT, "returns the MIDAS JSON-RPC schema JSON object");
4606 0 : return doc;
4607 : }
4608 :
4609 0 : return mjsonrpc_make_result(mjsonrpc_get_schema());
4610 : }
4611 :
4612 0 : static MJsonNode* js_get_timezone(const MJsonNode* params)
4613 : {
4614 0 : if (!params) {
4615 0 : MJSO *doc = MJSO::I();
4616 0 : doc->D("get current server timezone offset in seconds");
4617 0 : doc->P(NULL, 0, "there are no input parameters");
4618 0 : doc->R(NULL, MJSON_INT, "offset in seconds");
4619 0 : return doc;
4620 : }
4621 :
4622 0 : ss_tzset(); // required for localtime_r()
4623 0 : time_t rawtime = time(NULL);
4624 : struct tm gmt_tms;
4625 0 : gmtime_r(&rawtime, &gmt_tms);
4626 0 : time_t gmt = ss_mktime(&gmt_tms);
4627 : struct tm tms;
4628 0 : localtime_r(&rawtime, &tms);
4629 0 : time_t offset = rawtime - gmt + (tms.tm_isdst ? 3600 : 0);
4630 :
4631 0 : return mjsonrpc_make_result(MJsonNode::MakeNumber(offset));
4632 : }
4633 :
4634 :
4635 : /////////////////////////////////////////////////////////////////////////////////
4636 : //
4637 : // No RPC handlers beyound here
4638 : //
4639 : /////////////////////////////////////////////////////////////////////////////////
4640 :
4641 : struct MethodsTableEntry
4642 : {
4643 : mjsonrpc_handler_t* fHandler = NULL;
4644 : bool fNeedsLocking = false;
4645 : };
4646 :
4647 : typedef std::map<std::string, MethodsTableEntry> MethodsTable;
4648 : typedef MethodsTable::iterator MethodsTableIterator;
4649 :
4650 : static MethodsTable gMethodsTable;
4651 : static std::mutex* gMutex = NULL;
4652 :
4653 0 : void mjsonrpc_add_handler(const char* method, mjsonrpc_handler_t* handler, bool needs_locking)
4654 : {
4655 0 : MethodsTableEntry e;
4656 0 : e.fHandler = handler;
4657 0 : e.fNeedsLocking = needs_locking;
4658 0 : gMethodsTable[method] = e;
4659 0 : }
4660 :
4661 0 : void mjsonrpc_set_std_mutex(void* mutex)
4662 : {
4663 0 : gMutex = (std::mutex*)mutex;
4664 0 : }
4665 :
4666 0 : void mjsonrpc_init()
4667 : {
4668 0 : if (mjsonrpc_debug) {
4669 0 : printf("mjsonrpc_init!\n");
4670 : }
4671 :
4672 0 : if (!gNullNode)
4673 0 : gNullNode = MJsonNode::MakeNull();
4674 :
4675 : // test, debug and control methods for the rpc system
4676 0 : mjsonrpc_add_handler("null", xnull);
4677 0 : mjsonrpc_add_handler("get_debug", get_debug);
4678 0 : mjsonrpc_add_handler("set_debug", set_debug);
4679 0 : mjsonrpc_add_handler("get_sleep", get_sleep);
4680 0 : mjsonrpc_add_handler("set_sleep", set_sleep);
4681 0 : mjsonrpc_add_handler("get_time", get_time);
4682 0 : mjsonrpc_add_handler("set_time", set_time);
4683 0 : mjsonrpc_add_handler("get_schema", get_schema);
4684 : // interface to alarm functions
4685 0 : mjsonrpc_add_handler("al_reset_alarm", js_al_reset_alarm, true);
4686 0 : mjsonrpc_add_handler("al_trigger_alarm", js_al_trigger_alarm, true);
4687 0 : mjsonrpc_add_handler("al_trigger_class", js_al_trigger_class, true);
4688 : // interface to midas.c functions
4689 0 : mjsonrpc_add_handler("cm_exist", js_cm_exist, true);
4690 0 : mjsonrpc_add_handler("cm_msg_facilities", js_cm_msg_facilities);
4691 0 : mjsonrpc_add_handler("cm_msg_retrieve", js_cm_msg_retrieve);
4692 0 : mjsonrpc_add_handler("cm_msg1", js_cm_msg1);
4693 0 : mjsonrpc_add_handler("cm_shutdown", js_cm_shutdown, true);
4694 0 : mjsonrpc_add_handler("cm_transition", js_cm_transition, true);
4695 0 : mjsonrpc_add_handler("bm_receive_event", js_bm_receive_event, true);
4696 : // interface to odb functions
4697 0 : mjsonrpc_add_handler("db_copy", js_db_copy);
4698 0 : mjsonrpc_add_handler("db_paste", js_db_paste);
4699 0 : mjsonrpc_add_handler("db_get_values", js_db_get_values);
4700 0 : mjsonrpc_add_handler("db_ls", js_db_ls);
4701 0 : mjsonrpc_add_handler("db_create", js_db_create);
4702 0 : mjsonrpc_add_handler("db_delete", js_db_delete);
4703 0 : mjsonrpc_add_handler("db_resize", js_db_resize);
4704 0 : mjsonrpc_add_handler("db_resize_string", js_db_resize_string);
4705 0 : mjsonrpc_add_handler("db_rename", js_db_rename);
4706 0 : mjsonrpc_add_handler("db_scl", js_db_scl);
4707 0 : mjsonrpc_add_handler("db_sor", js_db_sor);
4708 0 : mjsonrpc_add_handler("db_link", js_db_link);
4709 0 : mjsonrpc_add_handler("db_reorder", js_db_reorder);
4710 0 : mjsonrpc_add_handler("db_key", js_db_key);
4711 : // interface to elog functions
4712 0 : mjsonrpc_add_handler("el_retrieve", js_el_retrieve, true);
4713 0 : mjsonrpc_add_handler("el_query", js_el_query, true);
4714 0 : mjsonrpc_add_handler("el_delete", js_el_delete, true);
4715 : // interface to midas history
4716 0 : mjsonrpc_add_handler("hs_get_active_events", js_hs_get_active_events, true);
4717 0 : mjsonrpc_add_handler("hs_get_channels", js_hs_get_channels, true);
4718 0 : mjsonrpc_add_handler("hs_get_events", js_hs_get_events, true);
4719 0 : mjsonrpc_add_handler("hs_get_tags", js_hs_get_tags, true);
4720 0 : mjsonrpc_add_handler("hs_get_last_written", js_hs_get_last_written, true);
4721 0 : mjsonrpc_add_handler("hs_reopen", js_hs_reopen, true);
4722 0 : mjsonrpc_add_handler("hs_read", js_hs_read, true);
4723 0 : mjsonrpc_add_handler("hs_read_binned", js_hs_read_binned, true);
4724 0 : mjsonrpc_add_handler("hs_read_arraybuffer", js_hs_read_arraybuffer, true);
4725 0 : mjsonrpc_add_handler("hs_read_binned_arraybuffer", js_hs_read_binned_arraybuffer, true);
4726 : // interface to image history
4727 0 : mjsonrpc_add_handler("hs_image_retrieve", js_hs_image_retrieve, true);
4728 : // sequencer and file_picker
4729 0 : mjsonrpc_add_handler("make_subdir", js_make_subdir, true);
4730 0 : mjsonrpc_add_handler("ext_list_files", js_ext_list_files, true);
4731 0 : mjsonrpc_add_handler("ext_save_file", js_ext_save_file, true);
4732 0 : mjsonrpc_add_handler("ext_read_file", js_ext_read_file, true);
4733 : // Read binary files of Uint8Arry
4734 0 : mjsonrpc_add_handler("read_binary_file", js_read_binary_file, true);
4735 : // interface to ss_system functions
4736 0 : mjsonrpc_add_handler("ss_millitime", js_ss_millitime);
4737 : // methods that perform computations or invoke actions
4738 0 : mjsonrpc_add_handler("get_alarms", get_alarms);
4739 : //mjsonrpc_add_handler("get_messages", get_messages);
4740 0 : mjsonrpc_add_handler("jrpc", jrpc);
4741 0 : mjsonrpc_add_handler("brpc", brpc);
4742 0 : mjsonrpc_add_handler("start_program", start_program);
4743 0 : mjsonrpc_add_handler("exec_script", exec_script);
4744 : // timezone function
4745 0 : mjsonrpc_add_handler("get_timezone", js_get_timezone);
4746 :
4747 0 : mjsonrpc_user_init();
4748 0 : }
4749 :
4750 0 : void mjsonrpc_exit()
4751 : {
4752 0 : if (mjsonrpc_debug) {
4753 0 : printf("mjsonrpc_exit!\n");
4754 : }
4755 :
4756 0 : js_hs_exit();
4757 0 : DeleteEventStash();
4758 0 : }
4759 :
4760 0 : static MJsonNode* mjsonrpc_make_schema(MethodsTable* h)
4761 : {
4762 0 : MJsonNode* s = MJsonNode::MakeObject();
4763 :
4764 0 : s->AddToObject("$schema", MJsonNode::MakeString("http://json-schema.org/schema#"));
4765 0 : s->AddToObject("id", MJsonNode::MakeString("MIDAS JSON-RPC autogenerated schema"));
4766 0 : s->AddToObject("title", MJsonNode::MakeString("MIDAS JSON-RPC schema"));
4767 0 : s->AddToObject("description", MJsonNode::MakeString("Autogenerated schema for all MIDAS JSON-RPC methods"));
4768 0 : s->AddToObject("type", MJsonNode::MakeString("object"));
4769 :
4770 0 : MJsonNode* m = MJsonNode::MakeObject();
4771 :
4772 0 : for (MethodsTableIterator iterator = h->begin(); iterator != h->end(); iterator++) {
4773 : // iterator->first = key
4774 : // iterator->second = value
4775 : //printf("build schema for method \"%s\"!\n", iterator->first.c_str());
4776 0 : MJsonNode* doc = iterator->second.fHandler(NULL);
4777 0 : if (doc == NULL)
4778 0 : doc = MJsonNode::MakeObject();
4779 0 : m->AddToObject(iterator->first.c_str(), doc);
4780 : }
4781 :
4782 0 : s->AddToObject("properties", m);
4783 0 : s->AddToObject("required", MJsonNode::MakeArray());
4784 :
4785 0 : return s;
4786 : }
4787 :
4788 0 : MJsonNode* mjsonrpc_get_schema()
4789 : {
4790 0 : return mjsonrpc_make_schema(&gMethodsTable);
4791 : }
4792 :
4793 : #ifdef MJSON_DEBUG
4794 : static void mjsonrpc_print_schema()
4795 : {
4796 : MJsonNode *s = mjsonrpc_get_schema();
4797 : s->Dump(0);
4798 : std::string str = s->Stringify(1);
4799 : printf("MJSON-RPC schema:\n");
4800 : printf("%s\n", str.c_str());
4801 : delete s;
4802 : }
4803 : #endif
4804 :
4805 0 : static std::string indent(int x, const char* p = " ")
4806 : {
4807 0 : if (x<1)
4808 0 : return "";
4809 0 : std::string s;
4810 0 : for (int i=0; i<x; i++)
4811 0 : s += p;
4812 0 : return s;
4813 0 : }
4814 :
4815 : struct NestedLine {
4816 : int nest;
4817 : bool span;
4818 : std::string text;
4819 : };
4820 :
4821 : class NestedOutput
4822 : {
4823 : public:
4824 : std::vector<NestedLine> fLines;
4825 : public:
4826 0 : void Clear()
4827 : {
4828 0 : fLines.clear();
4829 0 : }
4830 :
4831 0 : void Output(int nest, bool span, std::string text)
4832 : {
4833 0 : if (text.length() < 1)
4834 0 : return;
4835 :
4836 0 : NestedLine l;
4837 0 : l.nest = nest;
4838 0 : l.span = span;
4839 0 : l.text = text;
4840 0 : fLines.push_back(l);
4841 0 : };
4842 :
4843 0 : std::string Print()
4844 : {
4845 0 : std::vector<int> tablen;
4846 0 : std::vector<std::string> tab;
4847 0 : std::vector<std::string> tabx;
4848 :
4849 0 : tablen.push_back(0);
4850 0 : tab.push_back("");
4851 0 : tabx.push_back("");
4852 :
4853 0 : std::string xtab = "";
4854 0 : int maxlen = 0;
4855 0 : for (int n=0; ; n++) {
4856 0 : int len = -1;
4857 0 : for (unsigned i=0; i<fLines.size(); i++) {
4858 0 : int nn = fLines[i].nest;
4859 0 : bool pp = fLines[i].span;
4860 0 : if (pp)
4861 0 : continue;
4862 0 : if (nn != n)
4863 0 : continue;
4864 0 : int l = fLines[i].text.length();
4865 0 : if (l>len)
4866 0 : len = l;
4867 : }
4868 : //printf("nest %d len %d\n", n, len);
4869 0 : if (len < 0)
4870 0 : break; // nothing with this nest level
4871 0 : tablen.push_back(len);
4872 0 : tab.push_back(indent(len, " ") + " | ");
4873 0 : xtab += indent(len, " ") + " | ";
4874 0 : tabx.push_back(xtab);
4875 0 : maxlen += 3+len;
4876 0 : }
4877 :
4878 0 : std::string s;
4879 0 : int nest = 0;
4880 :
4881 0 : for (unsigned i=0; i<fLines.size(); i++) {
4882 0 : int n = fLines[i].nest;
4883 0 : bool p = fLines[i].span;
4884 :
4885 0 : std::string pad;
4886 :
4887 0 : if (!p) {
4888 0 : int ipad = tablen[n+1] - fLines[i].text.length();
4889 0 : pad = indent(ipad, " ");
4890 : }
4891 :
4892 0 : std::string hr = indent(maxlen-tabx[n].length(), "-");
4893 :
4894 0 : if (n > nest)
4895 0 : s += std::string(" | ") + fLines[i].text + pad;
4896 0 : else if (n == nest) {
4897 0 : s += "\n";
4898 0 : if (n == 0 || n == 1)
4899 0 : s += tabx[n] + hr + "\n";
4900 0 : s += tabx[n] + fLines[i].text + pad;
4901 : } else {
4902 0 : s += "\n";
4903 0 : if (n == 0 || n == 1)
4904 0 : s += tabx[n] + hr + "\n";
4905 0 : s += tabx[n] + fLines[i].text + pad;
4906 : }
4907 :
4908 0 : nest = n;
4909 0 : }
4910 :
4911 0 : return s;
4912 0 : }
4913 : };
4914 :
4915 : static std::string mjsonrpc_schema_to_html_anything(const MJsonNode* schema, int nest_level, NestedOutput* o);
4916 :
4917 0 : static std::string mjsonrpc_schema_to_html_object(const MJsonNode* schema, int nest_level, NestedOutput* o)
4918 : {
4919 0 : const MJsonNode* d = schema->FindObjectNode("description");
4920 0 : std::string description;
4921 0 : if (d)
4922 0 : description = d->GetString();
4923 :
4924 0 : std::string xshort = "object";
4925 0 : if (description.length() > 1)
4926 0 : xshort += "</td><td>" + description;
4927 :
4928 0 : const MJsonNode* properties = schema->FindObjectNode("properties");
4929 :
4930 0 : const MJsonNodeVector* required_list = NULL;
4931 0 : const MJsonNode* r = schema->FindObjectNode("required");
4932 0 : if (r)
4933 0 : required_list = r->GetArray();
4934 :
4935 0 : if (!properties) {
4936 0 : o->Output(nest_level, false, "object");
4937 0 : o->Output(nest_level+1, true, description);
4938 0 : return xshort;
4939 : }
4940 :
4941 0 : const MJsonStringVector *names = properties->GetObjectNames();
4942 0 : const MJsonNodeVector *nodes = properties->GetObjectNodes();
4943 :
4944 0 : if (!names || !nodes) {
4945 0 : o->Output(nest_level, false, "object");
4946 0 : o->Output(nest_level+1, true, description);
4947 0 : return xshort;
4948 : }
4949 :
4950 0 : std::string nest = indent(nest_level * 4);
4951 :
4952 0 : std::string s;
4953 :
4954 0 : s += nest + "<table border=1>\n";
4955 :
4956 0 : if (description.length() > 1) {
4957 0 : s += nest + "<tr>\n";
4958 0 : s += nest + " <td colspan=3>" + description + "</td>\n";
4959 0 : s += nest + "</tr>\n";
4960 : }
4961 :
4962 0 : o->Output(nest_level, true, description);
4963 :
4964 0 : for (unsigned i=0; i<names->size(); i++) {
4965 0 : std::string name = (*names)[i];
4966 0 : const MJsonNode* node = (*nodes)[i];
4967 :
4968 0 : bool required = false;
4969 0 : if (required_list)
4970 0 : for (unsigned j=0; j<required_list->size(); j++)
4971 0 : if ((*required_list)[j])
4972 0 : if ((*required_list)[j]->GetString() == name) {
4973 0 : required = true;
4974 0 : break;
4975 : }
4976 :
4977 0 : bool is_array = false;
4978 0 : const MJsonNode* type = node->FindObjectNode("type");
4979 0 : if (type && type->GetString() == "array")
4980 0 : is_array = true;
4981 :
4982 0 : if (is_array)
4983 0 : name += "[]";
4984 :
4985 0 : if (!required)
4986 0 : name += "?";
4987 :
4988 0 : o->Output(nest_level, false, name);
4989 :
4990 0 : s += nest + "<tr>\n";
4991 0 : s += nest + " <td>" + name + "</td>\n";
4992 0 : s += nest + " <td>";
4993 0 : s += mjsonrpc_schema_to_html_anything(node, nest_level + 1, o);
4994 0 : s += "</td>\n";
4995 0 : s += nest + "</tr>\n";
4996 0 : }
4997 :
4998 0 : s += nest + "</table>\n";
4999 :
5000 0 : return s;
5001 0 : }
5002 :
5003 0 : static std::string mjsonrpc_schema_to_html_array(const MJsonNode* schema, int nest_level, NestedOutput* o)
5004 : {
5005 0 : const MJsonNode* d = schema->FindObjectNode("description");
5006 0 : std::string description;
5007 0 : if (d)
5008 0 : description = d->GetString();
5009 :
5010 0 : std::string xshort = "array";
5011 0 : if (description.length() > 1)
5012 0 : xshort += "</td><td>" + description;
5013 :
5014 0 : const MJsonNode* items = schema->FindObjectNode("items");
5015 :
5016 0 : if (!items) {
5017 0 : o->Output(nest_level, false, "array");
5018 0 : o->Output(nest_level+1, true, description);
5019 0 : return xshort;
5020 : }
5021 :
5022 0 : const MJsonNodeVector *nodes = items->GetArray();
5023 :
5024 0 : if (!nodes) {
5025 0 : o->Output(nest_level, false, "array");
5026 0 : o->Output(nest_level+1, true, description);
5027 0 : return xshort;
5028 : }
5029 :
5030 0 : std::string nest = indent(nest_level * 4);
5031 :
5032 0 : std::string s;
5033 :
5034 : //s += "array</td><td>";
5035 :
5036 0 : s += nest + "<table border=1>\n";
5037 :
5038 0 : if (description.length() > 1) {
5039 0 : s += nest + "<tr>\n";
5040 0 : s += nest + " <td>" + description + "</td>\n";
5041 0 : s += nest + "</tr>\n";
5042 : }
5043 :
5044 0 : o->Output(nest_level, true, description);
5045 :
5046 0 : for (unsigned i=0; i<nodes->size(); i++) {
5047 0 : o->Output(nest_level, false, "array of");
5048 :
5049 0 : s += nest + "<tr>\n";
5050 0 : s += nest + " <td> array of " + mjsonrpc_schema_to_html_anything((*nodes)[i], nest_level + 1, o) + "</td>\n";
5051 0 : s += nest + "</tr>\n";
5052 : }
5053 :
5054 0 : s += nest + "</table>\n";
5055 :
5056 0 : return s;
5057 0 : }
5058 :
5059 0 : std::string mjsonrpc_schema_to_html_anything(const MJsonNode* schema, int nest_level, NestedOutput* o)
5060 : {
5061 0 : std::string type;
5062 0 : std::string description;
5063 : //bool optional = false;
5064 :
5065 0 : const MJsonNode* t = schema->FindObjectNode("type");
5066 0 : if (t)
5067 0 : type = t->GetString();
5068 : else
5069 0 : type = "any";
5070 :
5071 0 : const MJsonNode* d = schema->FindObjectNode("description");
5072 0 : if (d)
5073 0 : description = d->GetString();
5074 :
5075 : //const MJsonNode* o = schema->FindObjectNode("optional");
5076 : //if (o)
5077 : // optional = o->GetBool();
5078 :
5079 0 : if (type == "object") {
5080 0 : return mjsonrpc_schema_to_html_object(schema, nest_level, o);
5081 0 : } else if (type == "array") {
5082 0 : return mjsonrpc_schema_to_html_array(schema, nest_level, o);
5083 : } else {
5084 : //if (optional)
5085 : // output(nest_level, false, "?");
5086 : //else
5087 : // output(nest_level, false, "!");
5088 0 : o->Output(nest_level, false, type);
5089 0 : o->Output(nest_level+1, true, description);
5090 0 : if (description.length() > 1) {
5091 0 : return (type + "</td><td>" + description);
5092 : } else {
5093 0 : return (type);
5094 : }
5095 : }
5096 0 : }
5097 :
5098 0 : std::string mjsonrpc_schema_to_text(const MJsonNode* schema)
5099 : {
5100 0 : std::string s;
5101 0 : NestedOutput out;
5102 0 : out.Clear();
5103 0 : mjsonrpc_schema_to_html_anything(schema, 0, &out);
5104 : //s += "<pre>\n";
5105 : //s += nested_dump();
5106 : //s += "</pre>\n";
5107 0 : s += out.Print();
5108 0 : return s;
5109 0 : }
5110 :
5111 0 : static void add(std::string* s, const char* text)
5112 : {
5113 0 : assert(s != NULL);
5114 0 : if (s->length() > 0)
5115 0 : *s += ", ";
5116 0 : *s += text;
5117 0 : }
5118 :
5119 0 : static MJsonNode* mjsonrpc_handle_request(const MJsonNode* request)
5120 : {
5121 : // find required request elements
5122 0 : const MJsonNode* version = request->FindObjectNode("jsonrpc");
5123 0 : const MJsonNode* method = request->FindObjectNode("method");
5124 0 : const MJsonNode* params = request->FindObjectNode("params");
5125 0 : const MJsonNode* id = request->FindObjectNode("id");
5126 :
5127 0 : std::string bad = "";
5128 :
5129 0 : if (!version)
5130 0 : add(&bad, "jsonrpc version is missing");
5131 0 : if (!method)
5132 0 : add(&bad, "method is missing");
5133 0 : if (!params)
5134 0 : add(&bad, "params is missing");
5135 0 : if (!id)
5136 0 : add(&bad, "id is missing");
5137 :
5138 0 : if (version && (version->GetType() != MJSON_STRING))
5139 0 : add(&bad, "jsonrpc version is not a string");
5140 0 : if (version && (version->GetString() != "2.0"))
5141 0 : add(&bad, "jsonrpc version is not 2.0");
5142 :
5143 0 : if (method && (method->GetType() != MJSON_STRING))
5144 0 : add(&bad, "method is not a string");
5145 :
5146 0 : if (!method || bad.length() > 0) {
5147 0 : MJsonNode* response = mjsonrpc_make_error(-32600, "Invalid request", bad.c_str());
5148 0 : response->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
5149 0 : if (id) {
5150 0 : response->AddToObject("id", id->Copy());
5151 : } else {
5152 0 : response->AddToObject("id", MJsonNode::MakeNull());
5153 : }
5154 :
5155 0 : if (mjsonrpc_debug) {
5156 0 : printf("mjsonrpc: invalid request: reply:\n");
5157 0 : printf("%s\n", response->Stringify().c_str());
5158 0 : printf("\n");
5159 : }
5160 :
5161 0 : return response;
5162 : }
5163 :
5164 0 : double start_time = 0;
5165 :
5166 0 : if (mjsonrpc_time) {
5167 0 : start_time = GetTimeSec();
5168 : }
5169 :
5170 0 : const std::string ms = method->GetString();
5171 0 : const char* m = ms.c_str();
5172 :
5173 0 : MJsonNode* result = NULL;
5174 :
5175 : // special built-in methods
5176 :
5177 0 : if (strcmp(m, "echo") == 0) {
5178 0 : result = mjsonrpc_make_result(request->Copy());
5179 0 : } else if (strcmp(m, "error") == 0) {
5180 0 : result = mjsonrpc_make_error(1, "test error", "test error");
5181 0 : } else if (strcmp(m, "invalid_json") == 0) {
5182 0 : if (mjsonrpc_debug) {
5183 0 : printf("mjsonrpc: reply with invalid json\n");
5184 : }
5185 0 : return MJsonNode::MakeJSON("this is invalid json data");
5186 0 : } else if (strcmp(m, "test_nan_inf") == 0) {
5187 0 : double one = 1;
5188 0 : double zero = 0;
5189 0 : double nan = zero/zero;
5190 0 : double plusinf = one/zero;
5191 0 : double minusinf = -one/zero;
5192 0 : MJsonNode* n = MJsonNode::MakeArray();
5193 0 : n->AddToArray(MJsonNode::MakeNumber(nan));
5194 0 : n->AddToArray(MJsonNode::MakeNumber(plusinf));
5195 0 : n->AddToArray(MJsonNode::MakeNumber(minusinf));
5196 0 : result = mjsonrpc_make_result("test_nan_plusinf_minusinf", n);
5197 0 : } else if (strcmp(m, "test_arraybuffer") == 0) {
5198 0 : if (mjsonrpc_debug) {
5199 0 : printf("mjsonrpc: reply with test arraybuffer data\n");
5200 : }
5201 0 : size_t size = 32;
5202 0 : char* ptr = (char*)malloc(size);
5203 0 : assert(ptr != NULL);
5204 0 : for (size_t i=0; i<size; i++) {
5205 0 : ptr[i] = 'A' + i;
5206 : }
5207 0 : *((short*)(ptr+4*2*1)) = 111; // int16[4]
5208 0 : *((int*)(ptr+4*2*2)) = 1234; // int32[4]
5209 0 : *((double*)(ptr+4*2*3)) = 3.14; // float64[3]
5210 0 : return MJsonNode::MakeArrayBuffer(ptr, size);
5211 : } else {
5212 0 : MethodsTableIterator s = gMethodsTable.find(ms);
5213 0 : if (s != gMethodsTable.end()) {
5214 0 : bool lock = s->second.fNeedsLocking;
5215 0 : if (lock && gMutex)
5216 0 : gMutex->lock();
5217 0 : result = s->second.fHandler(params);
5218 0 : if (lock && gMutex)
5219 0 : gMutex->unlock();
5220 : } else {
5221 0 : result = mjsonrpc_make_error(-32601, "Method not found", (std::string("unknown method: ") + ms).c_str());
5222 : }
5223 : }
5224 :
5225 0 : if (mjsonrpc_debug) {
5226 0 : printf("mjsonrpc: handler reply:\n");
5227 0 : result->Dump();
5228 0 : printf("\n");
5229 : }
5230 :
5231 0 : double end_time = 0;
5232 0 : double elapsed_time = 0;
5233 0 : if (mjsonrpc_time) {
5234 0 : end_time = GetTimeSec();
5235 0 : elapsed_time = end_time - start_time;
5236 0 : if (mjsonrpc_time > 1) {
5237 0 : printf("request took %.3f seconds, method [%s]\n", elapsed_time, m);
5238 : }
5239 : }
5240 :
5241 0 : if (result->GetType() == MJSON_ARRAYBUFFER) {
5242 0 : return result;
5243 : }
5244 :
5245 0 : const MJsonNode *nerror = result->FindObjectNode("error");
5246 0 : const MJsonNode *nresult = result->FindObjectNode("result");
5247 :
5248 0 : if (nerror) {
5249 0 : result->DeleteObjectNode("result");
5250 0 : } else if (nresult) {
5251 0 : result->DeleteObjectNode("error");
5252 : } else {
5253 0 : delete result;
5254 0 : result = mjsonrpc_make_error(-32603, "Internal error", "bad dispatcher reply: no result and no error");
5255 : }
5256 :
5257 0 : result->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
5258 :
5259 0 : if (id) {
5260 0 : result->AddToObject("id", id->Copy());
5261 : } else {
5262 0 : result->AddToObject("id", MJsonNode::MakeNull());
5263 : }
5264 :
5265 0 : if (mjsonrpc_time) {
5266 0 : result->AddToObject("elapsed_time", MJsonNode::MakeNumber(elapsed_time));
5267 : }
5268 :
5269 0 : assert(result != NULL);
5270 :
5271 0 : return result;
5272 0 : }
5273 :
5274 0 : MJsonNode* mjsonrpc_decode_post_data(const char* post_data)
5275 : {
5276 : //printf("mjsonrpc call, data [%s]\n", post_data);
5277 0 : MJsonNode *request = MJsonNode::Parse(post_data);
5278 :
5279 0 : assert(request != NULL); // Parse never returns NULL - either parsed data or an MJSON_ERROR node
5280 :
5281 0 : if (mjsonrpc_debug) {
5282 0 : printf("mjsonrpc: request:\n");
5283 0 : request->Dump();
5284 0 : printf("\n");
5285 : }
5286 :
5287 0 : if (mjsonrpc_sleep) {
5288 0 : if (mjsonrpc_debug) {
5289 0 : printf("mjsonrpc: sleep %d\n", mjsonrpc_sleep);
5290 : }
5291 0 : for (int i=0; i<mjsonrpc_sleep; i++) {
5292 0 : sleep(1);
5293 : }
5294 : }
5295 :
5296 0 : if (request->GetType() == MJSON_ERROR) {
5297 0 : MJsonNode* reply = mjsonrpc_make_error(-32700, "Parse error", request->GetError().c_str());
5298 0 : reply->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
5299 0 : reply->AddToObject("id", MJsonNode::MakeNull());
5300 :
5301 0 : if (mjsonrpc_debug) {
5302 0 : printf("mjsonrpc: invalid json: reply:\n");
5303 0 : printf("%s\n", reply->Stringify().c_str());
5304 0 : printf("\n");
5305 : }
5306 :
5307 0 : delete request;
5308 0 : return reply;
5309 0 : } else if (request->GetType() == MJSON_OBJECT) {
5310 0 : MJsonNode* reply = mjsonrpc_handle_request(request);
5311 0 : delete request;
5312 0 : return reply;
5313 0 : } else if (request->GetType() == MJSON_ARRAY) {
5314 0 : const MJsonNodeVector* a = request->GetArray();
5315 :
5316 0 : if (a->size() < 1) {
5317 0 : MJsonNode* reply = mjsonrpc_make_error(-32600, "Invalid request", "batch request array has less than 1 element");
5318 0 : reply->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
5319 0 : reply->AddToObject("id", MJsonNode::MakeNull());
5320 :
5321 0 : if (mjsonrpc_debug) {
5322 0 : printf("mjsonrpc: invalid json: reply:\n");
5323 0 : printf("%s\n", reply->Stringify().c_str());
5324 0 : printf("\n");
5325 : }
5326 :
5327 0 : delete request;
5328 0 : return reply;
5329 : }
5330 :
5331 0 : MJsonNode* reply = MJsonNode::MakeArray();
5332 :
5333 0 : for (unsigned i=0; i<a->size(); i++) {
5334 0 : MJsonNode* r = mjsonrpc_handle_request(a->at(i));
5335 0 : reply->AddToArray(r);
5336 0 : if (r->GetType() == MJSON_ARRAYBUFFER) {
5337 0 : delete request;
5338 0 : delete reply;
5339 0 : reply = mjsonrpc_make_error(-32600, "Invalid request", "MJSON_ARRAYBUFFER return is not permitted for batch requests");
5340 0 : reply->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
5341 0 : reply->AddToObject("id", MJsonNode::MakeNull());
5342 0 : return reply;
5343 : }
5344 : }
5345 :
5346 0 : delete request;
5347 0 : return reply;
5348 : } else {
5349 0 : MJsonNode* reply = mjsonrpc_make_error(-32600, "Invalid request", "request is not a JSON object or JSON array");
5350 0 : reply->AddToObject("jsonrpc", MJsonNode::MakeString("2.0"));
5351 0 : reply->AddToObject("id", MJsonNode::MakeNull());
5352 :
5353 0 : if (mjsonrpc_debug) {
5354 0 : printf("mjsonrpc: invalid json: reply:\n");
5355 0 : printf("%s\n", reply->Stringify().c_str());
5356 0 : printf("\n");
5357 : }
5358 :
5359 0 : delete request;
5360 0 : return reply;
5361 : }
5362 : }
5363 :
5364 : /* emacs
5365 : * Local Variables:
5366 : * tab-width: 8
5367 : * c-basic-offset: 3
5368 : * indent-tabs-mode: nil
5369 : * End:
5370 : */
|