Line data Source code
1 : // Copyright (c) 2012-2024 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : // self
20 : //
21 : #include "instruction_factory.h"
22 : #include "state.h"
23 : #include "variable_address.h"
24 : #include "variable_array.h"
25 : #include "variable_floating_point.h"
26 : #include "variable_integer.h"
27 : #include "variable_list.h"
28 : #include "variable_regex.h"
29 : #include "variable_string.h"
30 : #include "variable_timestamp.h"
31 :
32 :
33 :
34 : // eventdispatcher
35 : //
36 : #include <eventdispatcher/connection_with_send_message.h>
37 : #include <eventdispatcher/exception.h>
38 : #include <eventdispatcher/signal.h>
39 : #include <eventdispatcher/signal_handler.h>
40 : #include <eventdispatcher/tcp_server_client_connection.h>
41 :
42 :
43 : // cppthread
44 : //
45 : #include <cppthread/thread.h>
46 :
47 :
48 : // advgetopt
49 : //
50 : #include <advgetopt/validator_double.h>
51 : #include <advgetopt/validator_integer.h>
52 :
53 :
54 : // snapdev
55 : //
56 : #include <snapdev/gethostname.h>
57 : #include <snapdev/hexadecimal_string.h>
58 : #include <snapdev/not_used.h>
59 : #include <snapdev/safe_stream.h>
60 : #include <snapdev/to_upper.h>
61 :
62 :
63 : // C++
64 : //
65 : #include <regex>
66 :
67 :
68 : // C
69 : //
70 : #include <poll.h>
71 : #include <signal.h>
72 :
73 :
74 : // last include
75 : //
76 : #include <snapdev/poison.h>
77 :
78 :
79 :
80 : namespace SNAP_CATCH2_NAMESPACE
81 : {
82 : namespace reporter
83 : {
84 :
85 :
86 : namespace
87 : {
88 :
89 :
90 :
91 : constexpr parameter_declaration const g_call_params[] =
92 : {
93 : {
94 : .f_name = "label",
95 : .f_type = "identifier",
96 : },
97 : {}
98 : };
99 :
100 :
101 : constexpr parameter_declaration const g_compare_params[] =
102 : {
103 : {
104 : .f_name = "expression",
105 : .f_type = "integer",
106 : .f_required = true,
107 : },
108 : {}
109 : };
110 :
111 :
112 : constexpr parameter_declaration const g_exit_params[] =
113 : {
114 : {
115 : .f_name = "error_message",
116 : .f_type = "string",
117 : .f_required = false,
118 : },
119 : {
120 : .f_name = "timeout",
121 : .f_type = "number",
122 : .f_required = false,
123 : },
124 : {}
125 : };
126 :
127 :
128 : constexpr parameter_declaration const g_goto_params[] =
129 : {
130 : {
131 : .f_name = "label",
132 : .f_type = "identifier",
133 : },
134 : {}
135 : };
136 :
137 :
138 : constexpr parameter_declaration const g_has_data_params[] =
139 : {
140 : {
141 : .f_name = "min_size",
142 : .f_type = "integer",
143 : .f_required = false,
144 : },
145 : {}
146 : };
147 :
148 :
149 : constexpr parameter_declaration const g_has_message_params[] =
150 : {
151 : {
152 : .f_name = "command",
153 : .f_type = "identifier",
154 : .f_required = false,
155 : },
156 : {}
157 : };
158 :
159 :
160 : constexpr parameter_declaration const g_has_type_params[] =
161 : {
162 : {
163 : .f_name = "name",
164 : .f_type = "identifier",
165 : },
166 : {
167 : .f_name = "type",
168 : .f_type = "identifier",
169 : },
170 : {}
171 : };
172 :
173 :
174 : constexpr parameter_declaration const g_hex_params[] =
175 : {
176 : {
177 : .f_name = "variable_name",
178 : .f_type = "identifier",
179 : },
180 : {
181 : .f_name = "value",
182 : .f_type = "integer",
183 : },
184 : {
185 : .f_name = "uppercase",
186 : .f_type = "integer",
187 : .f_required = false,
188 : },
189 : {
190 : .f_name = "width",
191 : .f_type = "integer",
192 : .f_required = false,
193 : },
194 : {}
195 : };
196 :
197 :
198 : constexpr parameter_declaration const g_hostname_params[] =
199 : {
200 : {
201 : .f_name = "variable_name",
202 : .f_type = "identifier",
203 : },
204 : {}
205 : };
206 :
207 :
208 : constexpr parameter_declaration const g_if_params[] =
209 : {
210 : {
211 : .f_name = "variable",
212 : .f_type = "identifier",
213 : .f_required = false,
214 : },
215 : {
216 : .f_name = "unordered",
217 : .f_type = "identifier",
218 : .f_required = false,
219 : },
220 : {
221 : .f_name = "ordered",
222 : .f_type = "identifier",
223 : .f_required = false,
224 : },
225 : {
226 : .f_name = "less",
227 : .f_type = "identifier",
228 : .f_required = false,
229 : },
230 : {
231 : .f_name = "less_or_equal",
232 : .f_type = "identifier",
233 : .f_required = false,
234 : },
235 : {
236 : .f_name = "greater",
237 : .f_type = "identifier",
238 : .f_required = false,
239 : },
240 : {
241 : .f_name = "greater_or_equal",
242 : .f_type = "identifier",
243 : .f_required = false,
244 : },
245 : {
246 : .f_name = "equal",
247 : .f_type = "identifier",
248 : .f_required = false,
249 : },
250 : {
251 : .f_name = "false",
252 : .f_type = "identifier",
253 : .f_required = false,
254 : },
255 : {
256 : .f_name = "not_equal",
257 : .f_type = "identifier",
258 : .f_required = false,
259 : },
260 : {
261 : .f_name = "true",
262 : .f_type = "identifier",
263 : .f_required = false,
264 : },
265 : {}
266 : };
267 :
268 :
269 : constexpr parameter_declaration const g_kill_params[] =
270 : {
271 : {
272 : .f_name = "signal",
273 : .f_type = "any",
274 : .f_required = false,
275 : },
276 : {}
277 : };
278 :
279 :
280 : constexpr parameter_declaration const g_label_params[] =
281 : {
282 : {
283 : .f_name = "name",
284 : .f_type = "identifier",
285 : },
286 : {}
287 : };
288 :
289 :
290 : constexpr parameter_declaration const g_listen_params[] =
291 : {
292 : {
293 : .f_name = "address",
294 : .f_type = "address",
295 : },
296 : {
297 : .f_name = "connection_type",
298 : .f_type = "identifier",
299 : .f_required = false,
300 : },
301 : {}
302 : };
303 :
304 :
305 : constexpr parameter_declaration const g_max_pid_params[] =
306 : {
307 : {
308 : .f_name = "variable_name",
309 : .f_type = "identifier",
310 : },
311 : {}
312 : };
313 :
314 :
315 : constexpr parameter_declaration const g_now_params[] =
316 : {
317 : {
318 : .f_name = "variable_name",
319 : .f_type = "identifier",
320 : },
321 : {}
322 : };
323 :
324 :
325 : constexpr parameter_declaration const g_print_params[] =
326 : {
327 : {
328 : .f_name = "message",
329 : .f_type = "string",
330 : },
331 : {}
332 : };
333 :
334 :
335 : constexpr parameter_declaration const g_random_params[] =
336 : {
337 : {
338 : .f_name = "variable_name",
339 : .f_type = "identifier",
340 : },
341 : {
342 : .f_name = "negative",
343 : .f_type = "integer",
344 : .f_required = false,
345 : },
346 : {}
347 : };
348 :
349 :
350 : constexpr parameter_declaration const g_save_parameter_value_params[] =
351 : {
352 : {
353 : .f_name = "parameter_name",
354 : .f_type = "identifier",
355 : },
356 : {
357 : .f_name = "variable_name",
358 : .f_type = "identifier",
359 : },
360 : {
361 : .f_name = "type",
362 : .f_type = "identifier",
363 : .f_required = false,
364 : },
365 : {}
366 : };
367 :
368 :
369 : constexpr parameter_declaration const g_send_data_params[] =
370 : {
371 : {
372 : .f_name = "values",
373 : .f_type = "array",
374 : },
375 : {}
376 : };
377 :
378 :
379 : constexpr parameter_declaration const g_send_message_params[] =
380 : {
381 : {
382 : .f_name = "sent_server",
383 : .f_type = "string_or_identifier",
384 : .f_required = false,
385 : },
386 : {
387 : .f_name = "sent_service",
388 : .f_type = "string_or_identifier",
389 : .f_required = false,
390 : },
391 : {
392 : .f_name = "server",
393 : .f_type = "string_or_identifier",
394 : .f_required = false,
395 : },
396 : {
397 : .f_name = "service",
398 : .f_type = "string_or_identifier",
399 : .f_required = false,
400 : },
401 : {
402 : .f_name = "command",
403 : .f_type = "identifier",
404 : },
405 : {
406 : .f_name = "parameters",
407 : .f_type = "list",
408 : .f_required = false,
409 : },
410 : {}
411 : };
412 :
413 :
414 : constexpr parameter_declaration const g_set_variable_params[] =
415 : {
416 : {
417 : .f_name = "name",
418 : .f_type = "identifier",
419 : },
420 : {
421 : .f_name = "value",
422 : .f_type = "any",
423 : },
424 : {
425 : .f_name = "type",
426 : .f_type = "identifier",
427 : .f_required = false,
428 : },
429 : {}
430 : };
431 :
432 :
433 : constexpr parameter_declaration const g_show_data_params[] =
434 : {
435 : {
436 : .f_name = "size",
437 : .f_type = "integer",
438 : },
439 : {}
440 : };
441 :
442 :
443 : constexpr parameter_declaration const g_sleep_params[] =
444 : {
445 : {
446 : .f_name = "seconds",
447 : .f_type = "number",
448 : },
449 : {}
450 : };
451 :
452 :
453 : constexpr parameter_declaration const g_sort_params[] =
454 : {
455 : {
456 : .f_name = "var1",
457 : .f_type = "string_or_identifier",
458 : },
459 : {
460 : .f_name = "var2",
461 : .f_type = "string_or_identifier",
462 : .f_required = false,
463 : },
464 : {
465 : .f_name = "var3",
466 : .f_type = "string_or_identifier",
467 : .f_required = false,
468 : },
469 : {
470 : .f_name = "var4",
471 : .f_type = "string_or_identifier",
472 : .f_required = false,
473 : },
474 : {
475 : .f_name = "var5",
476 : .f_type = "string_or_identifier",
477 : .f_required = false,
478 : },
479 : {
480 : .f_name = "var6",
481 : .f_type = "string_or_identifier",
482 : .f_required = false,
483 : },
484 : {
485 : .f_name = "var7",
486 : .f_type = "string_or_identifier",
487 : .f_required = false,
488 : },
489 : {
490 : .f_name = "var8",
491 : .f_type = "string_or_identifier",
492 : .f_required = false,
493 : },
494 : {
495 : .f_name = "var9",
496 : .f_type = "string_or_identifier",
497 : .f_required = false,
498 : },
499 : {
500 : .f_name = "var10",
501 : .f_type = "string_or_identifier",
502 : .f_required = false,
503 : },
504 : {
505 : .f_name = "var11",
506 : .f_type = "string_or_identifier",
507 : .f_required = false,
508 : },
509 : {
510 : .f_name = "var12",
511 : .f_type = "string_or_identifier",
512 : .f_required = false,
513 : },
514 : {
515 : .f_name = "var13",
516 : .f_type = "string_or_identifier",
517 : .f_required = false,
518 : },
519 : {
520 : .f_name = "var14",
521 : .f_type = "string_or_identifier",
522 : .f_required = false,
523 : },
524 : {
525 : .f_name = "var15",
526 : .f_type = "string_or_identifier",
527 : .f_required = false,
528 : },
529 : {
530 : .f_name = "var16",
531 : .f_type = "string_or_identifier",
532 : .f_required = false,
533 : },
534 : {
535 : .f_name = "var17",
536 : .f_type = "string_or_identifier",
537 : .f_required = false,
538 : },
539 : {
540 : .f_name = "var18",
541 : .f_type = "string_or_identifier",
542 : .f_required = false,
543 : },
544 : {
545 : .f_name = "var19",
546 : .f_type = "string_or_identifier",
547 : .f_required = false,
548 : },
549 : {
550 : .f_name = "var20",
551 : .f_type = "string_or_identifier",
552 : .f_required = false,
553 : },
554 : {}
555 : };
556 :
557 :
558 : constexpr parameter_declaration const g_strlen_params[] =
559 : {
560 : {
561 : .f_name = "variable_name",
562 : .f_type = "string_or_identifier",
563 : },
564 : {
565 : .f_name = "string",
566 : .f_type = "string",
567 : },
568 : {}
569 : };
570 :
571 :
572 : constexpr parameter_declaration const g_unset_variable_params[] =
573 : {
574 : {
575 : .f_name = "name",
576 : .f_type = "identifier",
577 : },
578 : {}
579 : };
580 :
581 :
582 : constexpr parameter_declaration const g_verify_data_params[] =
583 : {
584 : {
585 : .f_name = "values",
586 : .f_type = "array",
587 : },
588 : {}
589 : };
590 :
591 :
592 : constexpr parameter_declaration const g_verify_message_params[] =
593 : {
594 : {
595 : .f_name = "sent_server",
596 : .f_type = "any",
597 : .f_required = false,
598 : },
599 : {
600 : .f_name = "sent_service",
601 : .f_type = "any",
602 : .f_required = false,
603 : },
604 : {
605 : .f_name = "server",
606 : .f_type = "any",
607 : .f_required = false,
608 : },
609 : {
610 : .f_name = "service",
611 : .f_type = "any",
612 : .f_required = false,
613 : },
614 : {
615 : .f_name = "command",
616 : .f_type = "any",
617 : },
618 : {
619 : .f_name = "required_parameters",
620 : .f_type = "list",
621 : .f_required = false,
622 : },
623 : {
624 : .f_name = "optional_parameters",
625 : .f_type = "list",
626 : .f_required = false,
627 : },
628 : {
629 : .f_name = "forbidden_parameters",
630 : .f_type = "list",
631 : .f_required = false,
632 : },
633 : {}
634 : };
635 :
636 :
637 : constexpr parameter_declaration const g_wait_params[] =
638 : {
639 : {
640 : .f_name = "timeout",
641 : .f_type = "number",
642 : },
643 : {
644 : .f_name = "mode",
645 : .f_type = "identifier",
646 : .f_required = false,
647 : },
648 : {}
649 : };
650 :
651 :
652 : } // no name namespace
653 :
654 :
655 :
656 : // CALL
657 : //
658 : class inst_call
659 : : public instruction
660 : {
661 : public:
662 2 : inst_call()
663 6 : : instruction("call")
664 : {
665 2 : }
666 :
667 2 : virtual void func(state & s) override
668 : {
669 2 : s.push_ip();
670 :
671 6 : variable::pointer_t label_name(s.get_parameter("label", true));
672 2 : variable_string::pointer_t name(std::static_pointer_cast<variable_string>(label_name));
673 2 : ip_t const ip(s.get_label_position(name->get_string()));
674 2 : s.set_ip(ip);
675 4 : }
676 :
677 6 : virtual parameter_declaration const * parameter_declarations() const override
678 : {
679 6 : return g_call_params;
680 : }
681 :
682 : private:
683 : };
684 : INSTRUCTION(call);
685 :
686 :
687 : // CLEAR DATA
688 : //
689 : class inst_clear_data
690 : : public instruction
691 : {
692 : public:
693 2 : inst_clear_data()
694 6 : : instruction("clear_data")
695 : {
696 2 : }
697 :
698 8 : virtual void func(state & s) override
699 : {
700 8 : s.clear_data();
701 8 : }
702 :
703 : private:
704 : };
705 : INSTRUCTION(clear_data);
706 :
707 :
708 : // CLEAR MESSAGE
709 : //
710 : class inst_clear_message
711 : : public instruction
712 : {
713 : public:
714 2 : inst_clear_message()
715 6 : : instruction("clear_message")
716 : {
717 2 : }
718 :
719 59 : virtual void func(state & s) override
720 : {
721 59 : s.clear_message();
722 59 : }
723 :
724 : private:
725 : };
726 : INSTRUCTION(clear_message);
727 :
728 :
729 : // COMPARE
730 : //
731 : class inst_compare
732 : : public instruction
733 : {
734 : public:
735 2 : inst_compare()
736 6 : : instruction("compare")
737 : {
738 2 : }
739 :
740 27 : virtual void func(state & s) override
741 : {
742 81 : variable::pointer_t expr(s.get_parameter("expression", true));
743 27 : variable_integer::pointer_t integer(std::static_pointer_cast<variable_integer>(expr));
744 27 : int const value(integer->get_integer());
745 :
746 27 : if(value < -2 || value > 1)
747 : {
748 : throw ed::runtime_error(
749 2 : s.get_location()
750 4 : + "unsupported integer in compare(), values are limited to -2 to 1.");
751 : }
752 :
753 25 : s.set_compare(static_cast<compare_t>(value));
754 54 : }
755 :
756 89 : virtual parameter_declaration const * parameter_declarations() const override
757 : {
758 89 : return g_compare_params;
759 : }
760 :
761 : private:
762 : };
763 : INSTRUCTION(compare);
764 :
765 :
766 : // DISCONNECT
767 : //
768 : class inst_disconnect
769 : : public instruction
770 : {
771 : public:
772 2 : inst_disconnect()
773 6 : : instruction("disconnect")
774 : {
775 2 : }
776 :
777 4 : virtual void func(state & s) override
778 : {
779 4 : s.disconnect();
780 4 : }
781 :
782 : // at some point we may support a "name: <identifier>" parameter...
783 : //virtual parameter_declaration const * parameter_declarations() const override
784 : //{
785 : // return g_disconnect_params;
786 : //}
787 : };
788 : INSTRUCTION(disconnect);
789 :
790 :
791 : // EXIT
792 : //
793 : class inst_exit
794 : : public instruction
795 : {
796 : public:
797 2 : inst_exit()
798 6 : : instruction("exit")
799 : {
800 2 : }
801 :
802 26 : virtual void func(state & s) override
803 : {
804 26 : s.set_exit_code(0);
805 :
806 78 : variable::pointer_t timeout(s.get_parameter("timeout"));
807 78 : variable::pointer_t error_message(s.get_parameter("error_message"));
808 26 : if(error_message != nullptr)
809 : {
810 2 : if(timeout != nullptr)
811 : {
812 : throw ed::runtime_error(
813 1 : s.get_location()
814 2 : + "\"timeout\" and \"error_message\" from the exit() instruction are mutually exclusive.");
815 : }
816 :
817 1 : variable_string::pointer_t message(std::static_pointer_cast<variable_string>(error_message));
818 :
819 : // TODO: look at making the color optional
820 : //
821 : std::cerr
822 : << "\x1B[31m"
823 : << "error: "
824 1 : << message->get_string()
825 2 : << "\x1B[0m"
826 1 : << std::endl;
827 :
828 1 : s.set_exit_code(1);
829 1 : }
830 24 : else if(timeout != nullptr)
831 : {
832 : // wait for timeout seconds, if a message is received before
833 : // the wait times out, it failed
834 : //
835 3 : snapdev::timespec_ex timeout_duration;
836 3 : variable_integer::pointer_t int_seconds(std::dynamic_pointer_cast<variable_integer>(timeout));
837 3 : if(int_seconds == nullptr)
838 : {
839 2 : variable_floating_point::pointer_t flt_seconds(std::dynamic_pointer_cast<variable_floating_point>(timeout));
840 2 : timeout_duration.set(flt_seconds->get_floating_point());
841 2 : }
842 : else
843 : {
844 1 : timeout_duration.set(int_seconds->get_integer(), 0);
845 : }
846 3 : s.set_exit_code(poll(s, timeout_duration));
847 3 : }
848 :
849 : // jump to the very end so the executor knows it has to quit
850 : //
851 25 : s.set_ip(s.get_statement_size());
852 52 : }
853 :
854 3 : int poll(state & s, snapdev::timespec_ex timeout_duration)
855 : {
856 3 : std::vector<struct pollfd> fds;
857 3 : std::map<ed::connection *, int> position;
858 3 : ed::connection::vector_t connections(s.get_connections());
859 3 : ed::connection::pointer_t listen(s.get_listen_connection());
860 3 : if(listen != nullptr)
861 : {
862 2 : connections.push_back(listen);
863 : }
864 7 : for(auto & c : connections)
865 : {
866 4 : int e(0);
867 4 : if(c->is_listener() || c->is_signal())
868 : {
869 2 : e |= POLLIN;
870 : }
871 4 : if(c->is_reader())
872 : {
873 2 : e |= POLLIN | POLLPRI | POLLRDHUP;
874 : }
875 4 : if(c->is_writer())
876 : {
877 : e |= POLLOUT | POLLRDHUP; // LCOV_EXCL_LINE
878 : }
879 4 : if(e == 0)
880 : {
881 : continue; // LCOV_EXCL_LINE
882 : }
883 :
884 4 : position[c.get()] = fds.size();
885 4 : struct pollfd fd;
886 4 : fd.fd = c->get_socket();
887 4 : fd.events = e;
888 4 : fd.revents = 0;
889 4 : fds.push_back(fd);
890 : }
891 3 : if(fds.empty())
892 : {
893 : // no connection means we cannot receive invalid data before
894 : // exiting so all good here
895 : //
896 1 : return 0;
897 : }
898 :
899 2 : int const r(ppoll(&fds[0], fds.size(), &timeout_duration, nullptr));
900 2 : if(r < 0)
901 : {
902 : // LCOV_EXCL_START
903 : int const e(errno);
904 : throw ed::runtime_error(
905 : s.get_location()
906 : + "ppoll() returned an error: "
907 : + std::to_string(e)
908 : + ", "
909 : + strerror(e));
910 : // LCOV_EXCL_STOP
911 : }
912 4 : for(auto & c : connections)
913 : {
914 3 : struct pollfd const * fd(&fds[position[c.get()]]);
915 3 : if(fd->revents != 0)
916 : {
917 2 : if((fd->revents & (POLLHUP | POLLRDHUP)) != 0)
918 : {
919 : // hang ups are expected, so process them naturally
920 : //
921 1 : c->process_hup();
922 : }
923 : else
924 : {
925 1 : return 1;
926 : }
927 : }
928 : }
929 :
930 : // if no events happened, then we timed out which is good in this case
931 : //
932 1 : return 0;
933 3 : }
934 :
935 255 : virtual parameter_declaration const * parameter_declarations() const override
936 : {
937 255 : return g_exit_params;
938 : }
939 :
940 : private:
941 : };
942 : INSTRUCTION(exit);
943 :
944 :
945 : // GOTO
946 : //
947 : class inst_goto
948 : : public instruction
949 : {
950 : public:
951 2 : inst_goto()
952 6 : : instruction("goto")
953 : {
954 2 : }
955 :
956 4 : virtual void func(state & s) override
957 : {
958 12 : variable::pointer_t label_name(s.get_parameter("label", true));
959 4 : variable_string::pointer_t name(std::static_pointer_cast<variable_string>(label_name));
960 4 : ip_t const ip(s.get_label_position(name->get_string()));
961 4 : s.set_ip(ip);
962 8 : }
963 :
964 12 : virtual parameter_declaration const * parameter_declarations() const override
965 : {
966 12 : return g_goto_params;
967 : }
968 :
969 : private:
970 : };
971 : INSTRUCTION(goto);
972 :
973 :
974 : // HAS DATA
975 : //
976 : class inst_has_data
977 : : public instruction
978 : {
979 : public:
980 2 : inst_has_data()
981 6 : : instruction("has_data")
982 : {
983 2 : }
984 :
985 21 : virtual void func(state & s) override
986 : {
987 21 : ssize_t const size(s.data_size());
988 21 : bool has_data(size > 0);
989 :
990 21 : if(has_data)
991 : {
992 24 : variable::pointer_t min_size_var(s.get_parameter("min_size"));
993 8 : if(min_size_var != nullptr)
994 : {
995 8 : variable_integer::pointer_t min_size_int(std::static_pointer_cast<variable_integer>(min_size_var));
996 8 : has_data = size >= min_size_int->get_integer();
997 8 : }
998 8 : }
999 :
1000 21 : s.set_compare(has_data
1001 : ? compare_t::COMPARE_TRUE
1002 : : compare_t::COMPARE_FALSE);
1003 21 : }
1004 :
1005 47 : virtual parameter_declaration const * parameter_declarations() const override
1006 : {
1007 47 : return g_has_data_params;
1008 : }
1009 :
1010 : private:
1011 : };
1012 : INSTRUCTION(has_data);
1013 :
1014 :
1015 : // HAS MESSAGE
1016 : //
1017 : class inst_has_message
1018 : : public instruction
1019 : {
1020 : public:
1021 2 : inst_has_message()
1022 6 : : instruction("has_message")
1023 : {
1024 2 : }
1025 :
1026 65 : virtual void func(state & s) override
1027 : {
1028 65 : ed::message const msg(s.get_message());
1029 65 : std::string const & command(msg.get_command());
1030 65 : bool has_command(!command.empty());
1031 :
1032 65 : if(has_command)
1033 : {
1034 102 : variable::pointer_t command_name(s.get_parameter("command"));
1035 34 : if(command_name != nullptr)
1036 : {
1037 5 : variable_string::pointer_t name(std::static_pointer_cast<variable_string>(command_name));
1038 5 : has_command = command == name->get_string();
1039 5 : }
1040 34 : }
1041 65 : s.set_compare(has_command
1042 : ? compare_t::COMPARE_TRUE
1043 : : compare_t::COMPARE_FALSE);
1044 130 : }
1045 :
1046 105 : virtual parameter_declaration const * parameter_declarations() const override
1047 : {
1048 105 : return g_has_message_params;
1049 : }
1050 :
1051 : private:
1052 : };
1053 : INSTRUCTION(has_message);
1054 :
1055 :
1056 : // HAS TYPE
1057 : //
1058 : class inst_has_type
1059 : : public instruction
1060 : {
1061 : public:
1062 2 : inst_has_type()
1063 6 : : instruction("has_type")
1064 : {
1065 2 : }
1066 :
1067 5 : virtual void func(state & s) override
1068 : {
1069 15 : variable::pointer_t variable_name(s.get_parameter("name", true));
1070 5 : variable_string::pointer_t name(std::static_pointer_cast<variable_string>(variable_name));
1071 5 : variable::pointer_t var(s.get_variable(name->get_string()));
1072 :
1073 5 : if(var == nullptr)
1074 : {
1075 1 : s.set_compare(compare_t::COMPARE_UNORDERED);
1076 : }
1077 : else
1078 : {
1079 12 : variable::pointer_t variable_type(s.get_parameter("type", true));
1080 4 : variable_string::pointer_t type(std::static_pointer_cast<variable_string>(variable_type));
1081 4 : s.set_compare(var->get_type() == type->get_string()
1082 : ? compare_t::COMPARE_TRUE
1083 : : compare_t::COMPARE_FALSE);
1084 4 : }
1085 10 : }
1086 :
1087 20 : virtual parameter_declaration const * parameter_declarations() const override
1088 : {
1089 20 : return g_has_type_params;
1090 : }
1091 :
1092 : private:
1093 : };
1094 : INSTRUCTION(has_type);
1095 :
1096 :
1097 : // HEX
1098 : //
1099 : class inst_hex
1100 : : public instruction
1101 : {
1102 : public:
1103 2 : inst_hex()
1104 6 : : instruction("hex")
1105 : {
1106 2 : }
1107 :
1108 5 : virtual void func(state & s) override
1109 : {
1110 15 : variable::pointer_t var_name(s.get_parameter("variable_name", true));
1111 5 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(var_name));
1112 5 : std::string const & variable_name(var->get_string());
1113 :
1114 15 : variable::pointer_t i(s.get_parameter("value", true));
1115 5 : variable_integer::pointer_t integer(std::dynamic_pointer_cast<variable_integer>(i));
1116 5 : int const value(integer->get_integer());
1117 :
1118 5 : bool uppercase_flag(false);
1119 15 : variable::pointer_t uppercase(s.get_parameter("uppercase"));
1120 5 : if(uppercase != nullptr)
1121 : {
1122 3 : variable_integer::pointer_t uc(std::dynamic_pointer_cast<variable_integer>(uppercase));
1123 3 : uppercase_flag = uc->get_integer() != 0;
1124 3 : }
1125 :
1126 5 : int width_value(1);
1127 15 : variable::pointer_t w(s.get_parameter("width"));
1128 5 : variable_integer::pointer_t width(std::dynamic_pointer_cast<variable_integer>(w));
1129 5 : if(width != nullptr)
1130 : {
1131 2 : width_value = width->get_integer();
1132 : }
1133 :
1134 5 : variable_string::pointer_t new_var(std::make_shared<variable_string>(variable_name));
1135 5 : new_var->set_string(snapdev::int_to_hex(value, uppercase_flag, width_value));
1136 5 : s.set_variable(new_var);
1137 10 : }
1138 :
1139 25 : virtual parameter_declaration const * parameter_declarations() const override
1140 : {
1141 25 : return g_hex_params;
1142 : }
1143 :
1144 : private:
1145 : };
1146 : INSTRUCTION(hex);
1147 :
1148 :
1149 : // HOSTNAME
1150 : //
1151 : class inst_hostname
1152 : : public instruction
1153 : {
1154 : public:
1155 2 : inst_hostname()
1156 6 : : instruction("hostname")
1157 : {
1158 2 : }
1159 :
1160 1 : virtual void func(state & s) override
1161 : {
1162 3 : variable::pointer_t param(s.get_parameter("variable_name", true));
1163 1 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1164 1 : std::string const & variable_name(var->get_string());
1165 :
1166 1 : variable_string::pointer_t new_var(std::make_shared<variable_string>(variable_name, "string"));
1167 1 : new_var->set_string(snapdev::gethostname());
1168 1 : s.set_variable(new_var);
1169 2 : }
1170 :
1171 3 : virtual parameter_declaration const * parameter_declarations() const override
1172 : {
1173 3 : return g_hostname_params;
1174 : }
1175 :
1176 : private:
1177 : };
1178 : INSTRUCTION(hostname);
1179 :
1180 :
1181 : // IF
1182 : //
1183 : class inst_if
1184 : : public instruction
1185 : {
1186 : public:
1187 2 : inst_if()
1188 6 : : instruction("if")
1189 : {
1190 2 : }
1191 :
1192 151 : virtual void func(state & s) override
1193 : {
1194 : // TODO: verify potential overlaps (i.e. if the instruction has
1195 : // multiple labels and we could have the choice between
1196 : // two or more in variable situations)
1197 : //
1198 151 : variable::pointer_t label_name;
1199 151 : compare_t compare(compare_t::COMPARE_UNDEFINED);
1200 453 : variable::pointer_t const var_name(s.get_parameter("variable"));
1201 151 : if(var_name != nullptr)
1202 : {
1203 34 : variable_string::pointer_t name(std::dynamic_pointer_cast<variable_string>(var_name));
1204 34 : variable::pointer_t value(s.get_variable(name->get_string()));
1205 34 : if(value != nullptr)
1206 : {
1207 33 : std::string const & type(value->get_type());
1208 33 : if(type == "integer")
1209 : {
1210 16 : variable_integer::pointer_t int_value(std::static_pointer_cast<variable_integer>(value));
1211 16 : int const v(int_value->get_integer());
1212 16 : if(v == 0)
1213 : {
1214 5 : compare = compare_t::COMPARE_EQUAL;
1215 : }
1216 11 : else if(v < 0)
1217 : {
1218 5 : compare = compare_t::COMPARE_LESS;
1219 : }
1220 : else
1221 : {
1222 6 : compare = compare_t::COMPARE_GREATER;
1223 : }
1224 16 : }
1225 17 : else if(type == "floating_point")
1226 : {
1227 16 : variable_floating_point::pointer_t int_value(std::static_pointer_cast<variable_floating_point>(value));
1228 16 : double const v(int_value->get_floating_point());
1229 : #pragma GCC diagnostic push
1230 : #pragma GCC diagnostic ignored "-Wfloat-equal"
1231 16 : if(std::isnan(v))
1232 : {
1233 1 : compare = compare_t::COMPARE_UNORDERED;
1234 : }
1235 15 : else if(v == 0.0)
1236 : {
1237 5 : compare = compare_t::COMPARE_EQUAL;
1238 : }
1239 10 : else if(v < 0.0)
1240 : {
1241 5 : compare = compare_t::COMPARE_LESS;
1242 : }
1243 : else
1244 : {
1245 5 : compare = compare_t::COMPARE_GREATER;
1246 : }
1247 : #pragma GCC diagnostic pop
1248 16 : }
1249 : else
1250 : {
1251 3 : throw ed::runtime_error("if(variable: ...) only supports variables of type integer or floating point.");
1252 : }
1253 : }
1254 : else
1255 : {
1256 1 : compare = compare_t::COMPARE_UNORDERED;
1257 : }
1258 35 : }
1259 : else
1260 : {
1261 117 : compare = s.get_compare();
1262 : }
1263 149 : switch(compare)
1264 : {
1265 : // LCOV_EXCL_START
1266 : case compare_t::COMPARE_UNDEFINED:
1267 : // this cannot happen since we already throw in get_compare()
1268 : // and in case of a variable, we throw if we get an invalid type
1269 : //
1270 : throw ed::implementation_error("got undefined compare in inst_if::func"); // LCOV_EXCL_LINE
1271 : // LCOV_EXCL_STOP
1272 :
1273 3 : case compare_t::COMPARE_UNORDERED:
1274 9 : label_name = s.get_parameter("unordered");
1275 3 : break;
1276 :
1277 19 : case compare_t::COMPARE_LESS:
1278 57 : label_name = s.get_parameter("less");
1279 19 : if(label_name == nullptr)
1280 : {
1281 24 : label_name = s.get_parameter("less_or_equal");
1282 8 : if(label_name == nullptr)
1283 : {
1284 18 : label_name = s.get_parameter("not_equal");
1285 6 : if(label_name == nullptr)
1286 : {
1287 12 : label_name = s.get_parameter("true");
1288 4 : if(label_name == nullptr)
1289 : {
1290 6 : label_name = s.get_parameter("ordered");
1291 : }
1292 : }
1293 : }
1294 : }
1295 19 : break;
1296 :
1297 64 : case compare_t::COMPARE_EQUAL:
1298 192 : label_name = s.get_parameter("equal");
1299 64 : if(label_name == nullptr)
1300 : {
1301 165 : label_name = s.get_parameter("less_or_equal");
1302 55 : if(label_name == nullptr)
1303 : {
1304 159 : label_name = s.get_parameter("greater_or_equal");
1305 53 : if(label_name == nullptr)
1306 : {
1307 153 : label_name = s.get_parameter("false");
1308 51 : if(label_name == nullptr)
1309 : {
1310 21 : label_name = s.get_parameter("ordered");
1311 : }
1312 : }
1313 : }
1314 : }
1315 64 : break;
1316 :
1317 63 : case compare_t::COMPARE_GREATER:
1318 189 : label_name = s.get_parameter("greater");
1319 63 : if(label_name == nullptr)
1320 : {
1321 156 : label_name = s.get_parameter("greater_or_equal");
1322 52 : if(label_name == nullptr)
1323 : {
1324 150 : label_name = s.get_parameter("not_equal");
1325 50 : if(label_name == nullptr)
1326 : {
1327 144 : label_name = s.get_parameter("true");
1328 48 : if(label_name == nullptr)
1329 : {
1330 132 : label_name = s.get_parameter("ordered");
1331 : }
1332 : }
1333 : }
1334 : }
1335 63 : break;
1336 :
1337 : }
1338 :
1339 : // if a matching label was found, act on it
1340 : //
1341 149 : if(label_name != nullptr)
1342 : {
1343 102 : variable_string::pointer_t name(std::static_pointer_cast<variable_string>(label_name));
1344 102 : ip_t const ip(s.get_label_position(name->get_string()));
1345 102 : s.set_ip(ip);
1346 102 : }
1347 302 : }
1348 :
1349 423 : virtual parameter_declaration const * parameter_declarations() const override
1350 : {
1351 423 : return g_if_params;
1352 : }
1353 : };
1354 : INSTRUCTION(if);
1355 :
1356 :
1357 : // KILL
1358 : //
1359 : class inst_kill
1360 : : public instruction
1361 : {
1362 : public:
1363 2 : inst_kill()
1364 6 : : instruction("kill")
1365 : {
1366 2 : }
1367 :
1368 6 : virtual void func(state & s) override
1369 : {
1370 6 : int sig(SIGINT);
1371 18 : variable::pointer_t const signal_name(s.get_parameter("signal"));
1372 6 : if(signal_name != nullptr)
1373 : {
1374 6 : std::string const & type(signal_name->get_type());
1375 6 : if(type == "integer")
1376 : {
1377 2 : sig = std::static_pointer_cast<variable_integer>(signal_name)->get_integer();
1378 : }
1379 4 : else if(type == "string" || type == "identifier")
1380 : {
1381 3 : std::string const name(std::dynamic_pointer_cast<variable_string>(signal_name)->get_string());
1382 3 : sig = ed::signal_handler::get_signal_number(snapdev::to_upper(name));
1383 3 : }
1384 : else
1385 : {
1386 : throw ed::runtime_error(
1387 1 : s.get_location()
1388 2 : + "kill(signal: ...) unsupported parameter type.");
1389 : }
1390 5 : if(sig < SIGHUP || sig >= NSIG)
1391 : {
1392 : throw ed::runtime_error(
1393 2 : s.get_location()
1394 4 : + "kill(signal: ...) unknown signal.");
1395 : }
1396 : }
1397 :
1398 : // send the signal to the main thread
1399 : //
1400 3 : if(pthread_kill(s.get_server_thread_id(), sig) != 0)
1401 : {
1402 : // LCOV_EXCL_START
1403 : int const e(errno);
1404 : throw ed::runtime_error(
1405 : s.get_location()
1406 : + "kill(): signal could not be sent (errno: "
1407 : + std::to_string(e)
1408 : + ", "
1409 : + strerror(e)
1410 : + ").");
1411 : // LCOV_EXCL_STOP
1412 : }
1413 9 : }
1414 :
1415 18 : virtual parameter_declaration const * parameter_declarations() const override
1416 : {
1417 18 : return g_kill_params;
1418 : }
1419 : };
1420 : INSTRUCTION(kill);
1421 :
1422 :
1423 : // LABEL
1424 : //
1425 : class inst_label
1426 : : public instruction
1427 : {
1428 : public:
1429 2 : inst_label()
1430 6 : : instruction("label")
1431 : {
1432 2 : }
1433 :
1434 151 : virtual void func(state & s) override
1435 : {
1436 151 : snapdev::NOT_USED(s);
1437 151 : }
1438 :
1439 401 : virtual parameter_declaration const * parameter_declarations() const override
1440 : {
1441 401 : return g_label_params;
1442 : }
1443 : };
1444 : INSTRUCTION(label);
1445 :
1446 :
1447 : // LISTEN
1448 : //
1449 : class inst_listen
1450 : : public instruction
1451 : {
1452 : public:
1453 2 : inst_listen()
1454 6 : : instruction("listen")
1455 : {
1456 2 : }
1457 :
1458 38 : virtual void func(state & s) override
1459 : {
1460 : // reset to default type
1461 : //
1462 38 : s.set_connection_type(connection_type_t::CONNECTION_TYPE_MESSENGER);
1463 :
1464 : // get user defined type (optional)
1465 : //
1466 114 : variable::pointer_t param(s.get_parameter("connection_type", false));
1467 38 : if(param != nullptr)
1468 : {
1469 11 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1470 11 : std::string const & connection_type(var->get_string());
1471 11 : if(connection_type == "tcp")
1472 : {
1473 8 : s.set_connection_type(connection_type_t::CONNECTION_TYPE_TCP);
1474 : }
1475 3 : else if(connection_type == "messenger")
1476 : {
1477 2 : s.set_connection_type(connection_type_t::CONNECTION_TYPE_MESSENGER);
1478 : }
1479 : else
1480 : {
1481 : throw ed::runtime_error(
1482 : "unknown type \""
1483 2 : + connection_type
1484 3 : + "\" for listen().");
1485 : }
1486 11 : }
1487 :
1488 111 : variable::pointer_t address(s.get_parameter("address", true));
1489 40 : s.listen(std::static_pointer_cast<variable_address>(address)->get_address());
1490 75 : }
1491 :
1492 125 : virtual parameter_declaration const * parameter_declarations() const override
1493 : {
1494 125 : return g_listen_params;
1495 : }
1496 : };
1497 : INSTRUCTION(listen);
1498 :
1499 :
1500 : // MAX_PID
1501 : //
1502 : class inst_max_pid
1503 : : public instruction
1504 : {
1505 : public:
1506 2 : inst_max_pid()
1507 6 : : instruction("max_pid")
1508 : {
1509 2 : }
1510 :
1511 1 : virtual void func(state & s) override
1512 : {
1513 3 : variable::pointer_t param(s.get_parameter("variable_name", true));
1514 1 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1515 1 : std::string const & variable_name(var->get_string());
1516 :
1517 1 : variable_integer::pointer_t new_var(std::make_shared<variable_integer>(variable_name));
1518 1 : new_var->set_integer(cppthread::get_pid_max());
1519 1 : s.set_variable(new_var);
1520 2 : }
1521 :
1522 3 : virtual parameter_declaration const * parameter_declarations() const override
1523 : {
1524 3 : return g_max_pid_params;
1525 : }
1526 :
1527 : private:
1528 : };
1529 : INSTRUCTION(max_pid);
1530 :
1531 :
1532 : // NOW
1533 : //
1534 : class inst_now
1535 : : public instruction
1536 : {
1537 : public:
1538 2 : inst_now()
1539 6 : : instruction("now")
1540 : {
1541 2 : }
1542 :
1543 2 : virtual void func(state & s) override
1544 : {
1545 6 : variable::pointer_t param(s.get_parameter("variable_name", true));
1546 2 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1547 2 : std::string const & variable_name(var->get_string());
1548 :
1549 2 : variable_timestamp::pointer_t new_var(std::make_shared<variable_timestamp>(variable_name));
1550 2 : new_var->set_timestamp(snapdev::now());
1551 2 : s.set_variable(new_var);
1552 4 : }
1553 :
1554 6 : virtual parameter_declaration const * parameter_declarations() const override
1555 : {
1556 6 : return g_now_params;
1557 : }
1558 :
1559 : private:
1560 : };
1561 : INSTRUCTION(now);
1562 :
1563 :
1564 : // PRINT
1565 : //
1566 : class inst_print
1567 : : public instruction
1568 : {
1569 : public:
1570 2 : inst_print()
1571 6 : : instruction("print")
1572 : {
1573 2 : }
1574 :
1575 2 : virtual void func(state & s) override
1576 : {
1577 6 : variable::pointer_t msg(s.get_parameter("message", true));
1578 : std::cout
1579 : << "--- message: "
1580 6 : << std::static_pointer_cast<variable_string>(msg)->get_string()
1581 4 : << std::endl;
1582 4 : }
1583 :
1584 13 : virtual parameter_declaration const * parameter_declarations() const override
1585 : {
1586 13 : return g_print_params;
1587 : }
1588 : };
1589 : INSTRUCTION(print);
1590 :
1591 :
1592 : // RANDOM
1593 : //
1594 : class inst_random
1595 : : public instruction
1596 : {
1597 : public:
1598 2 : inst_random()
1599 6 : : instruction("random")
1600 : {
1601 2 : }
1602 :
1603 3 : virtual void func(state & s) override
1604 : {
1605 9 : variable::pointer_t param(s.get_parameter("variable_name", true));
1606 3 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1607 3 : std::string const & variable_name(var->get_string());
1608 :
1609 3 : bool negative(true);
1610 9 : param = s.get_parameter("negative", false);
1611 3 : if(param != nullptr)
1612 : {
1613 2 : variable_integer::pointer_t var_int = std::static_pointer_cast<variable_integer>(param);
1614 2 : std::int64_t const & boolean(var_int->get_integer());
1615 2 : negative = boolean != 0;
1616 2 : }
1617 :
1618 3 : variable_integer::pointer_t new_var(std::make_shared<variable_integer>(variable_name));
1619 : std::int64_t result(
1620 3 : (static_cast<std::int64_t>(rand()) << 48)
1621 3 : ^ (static_cast<std::int64_t>(rand()) << 32)
1622 3 : ^ (static_cast<std::int64_t>(rand()) << 16)
1623 3 : ^ (static_cast<std::int64_t>(rand()) << 0));
1624 3 : if(!negative)
1625 : {
1626 : // remove the sign bit
1627 : //
1628 1 : result &= 0x7FFFFFFFFFFFFFFFLL;
1629 : }
1630 3 : new_var->set_integer(result);
1631 3 : s.set_variable(new_var);
1632 6 : }
1633 :
1634 11 : virtual parameter_declaration const * parameter_declarations() const override
1635 : {
1636 11 : return g_random_params;
1637 : }
1638 :
1639 : private:
1640 : };
1641 : INSTRUCTION(random);
1642 :
1643 :
1644 : // RETURN
1645 : //
1646 : class inst_return
1647 : : public instruction
1648 : {
1649 : public:
1650 2 : inst_return()
1651 6 : : instruction("return")
1652 : {
1653 2 : }
1654 :
1655 2 : virtual void func(state & s) override
1656 : {
1657 2 : s.pop_ip();
1658 2 : }
1659 :
1660 : private:
1661 : };
1662 : INSTRUCTION(return);
1663 :
1664 :
1665 : // RUN
1666 : //
1667 : class inst_run
1668 : : public instruction
1669 : {
1670 : public:
1671 2 : inst_run()
1672 6 : : instruction("run")
1673 : {
1674 2 : }
1675 :
1676 2 : virtual void func(state & s) override
1677 : {
1678 2 : snapdev::NOT_USED(s);
1679 6 : throw ed::implementation_error("run::func() was called when it should be intercepted by the executor.");
1680 : }
1681 :
1682 : private:
1683 : };
1684 : INSTRUCTION(run);
1685 :
1686 :
1687 : // SAVE PARAMETER VALUE
1688 : //
1689 : class inst_save_parameter_value
1690 : : public instruction
1691 : {
1692 : public:
1693 2 : inst_save_parameter_value()
1694 6 : : instruction("save_parameter_value")
1695 : {
1696 2 : }
1697 :
1698 15 : virtual void func(state & s) override
1699 : {
1700 15 : ed::message const msg(s.get_message());
1701 :
1702 45 : variable::pointer_t param(s.get_parameter("parameter_name", true));
1703 15 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1704 15 : std::string const & parameter_name(var->get_string());
1705 15 : std::string value;
1706 15 : if(msg.has_parameter(parameter_name))
1707 : {
1708 8 : value = msg.get_parameter(parameter_name);
1709 : }
1710 7 : else if(parameter_name == "sent_server")
1711 : {
1712 1 : value = msg.get_sent_from_server();
1713 : }
1714 6 : else if(parameter_name == "sent_service")
1715 : {
1716 1 : value = msg.get_sent_from_service();
1717 : }
1718 5 : else if(parameter_name == "server")
1719 : {
1720 1 : value = msg.get_server();
1721 : }
1722 4 : else if(parameter_name == "service")
1723 : {
1724 1 : value = msg.get_service();
1725 : }
1726 3 : else if(parameter_name == "command")
1727 : {
1728 1 : value = msg.get_command();
1729 : }
1730 :
1731 45 : param = s.get_parameter("variable_name", true);
1732 15 : var = std::static_pointer_cast<variable_string>(param);
1733 15 : std::string const & variable_name(var->get_string());
1734 :
1735 45 : std::string type("string");
1736 45 : param = s.get_parameter("type");
1737 15 : if(param != nullptr)
1738 : {
1739 13 : var = std::static_pointer_cast<variable_string>(param);
1740 13 : type = var->get_string();
1741 : }
1742 15 : if(type == "string"
1743 15 : || type == "identifier")
1744 : {
1745 7 : variable_string::pointer_t new_var(std::make_shared<variable_string>(variable_name, type));
1746 7 : new_var->set_string(value);
1747 7 : s.set_variable(new_var);
1748 7 : }
1749 8 : else if(type == "integer")
1750 : {
1751 5 : variable_integer::pointer_t new_var(std::make_shared<variable_integer>(variable_name));
1752 5 : std::int64_t int_value(0);
1753 5 : if(!value.empty()
1754 5 : && !advgetopt::validator_integer::convert_string(value, int_value))
1755 : {
1756 : throw ed::runtime_error(
1757 : "value \""
1758 2 : + value
1759 3 : + "\" not recognized as a valid integer.");
1760 : }
1761 4 : new_var->set_integer(int_value);
1762 4 : s.set_variable(new_var);
1763 5 : }
1764 3 : else if(type == "timestamp")
1765 : {
1766 2 : variable_timestamp::pointer_t new_var(std::make_shared<variable_timestamp>(variable_name));
1767 2 : if(!value.empty())
1768 : {
1769 1 : new_var->set_timestamp(value);
1770 : }
1771 2 : s.set_variable(new_var);
1772 2 : }
1773 : else
1774 : {
1775 : throw ed::runtime_error(
1776 : "unsupported type \""
1777 2 : + type
1778 3 : + "\" for save_parameter_value().");
1779 : }
1780 36 : }
1781 :
1782 105 : virtual parameter_declaration const * parameter_declarations() const override
1783 : {
1784 105 : return g_save_parameter_value_params;
1785 : }
1786 :
1787 : private:
1788 : };
1789 : INSTRUCTION(save_parameter_value);
1790 :
1791 :
1792 : // SEND DATA
1793 : //
1794 : class inst_send_data
1795 : : public instruction
1796 : {
1797 : public:
1798 2 : inst_send_data()
1799 6 : : instruction("send_data")
1800 : {
1801 2 : }
1802 :
1803 6 : virtual void func(state & s) override
1804 : {
1805 6 : ed::connection::vector_t v(s.get_connections());
1806 6 : if(v.empty())
1807 : {
1808 3 : throw ed::runtime_error("send_data() has no connection to send data.");
1809 : }
1810 : // TODO: fix the connection selection, if we have more than one,
1811 : // how do we know which one to select? (i.e. have a connection
1812 : // name included in the parameters)
1813 : //
1814 5 : ed::tcp_server_client_connection::pointer_t c(std::dynamic_pointer_cast<ed::tcp_server_client_connection>(v[0]));
1815 5 : if(c == nullptr)
1816 : {
1817 : throw ed::runtime_error("send_data() called without a valid listener connection."); // LCOV_EXCL_LINE
1818 : }
1819 :
1820 15 : variable::pointer_t param(s.get_parameter("values", true));
1821 5 : variable_array::pointer_t var(std::static_pointer_cast<variable_array>(param));
1822 5 : std::size_t const size(var->get_item_size());
1823 5 : if(size == 0)
1824 : {
1825 : // at this point, there is no reason for us to "send" an empty buffer
1826 : // since that would do absolutely nothing
1827 : //
1828 : throw ed::runtime_error(
1829 1 : s.get_location()
1830 2 : + "array cannot be empty.");
1831 : }
1832 4 : connection_data_t buf;
1833 28 : for(std::size_t i(0); i < size; ++i)
1834 : {
1835 26 : variable_integer::pointer_t value(std::static_pointer_cast<variable_integer>(var->get_item(i)));
1836 26 : std::int64_t const byte(value->get_integer());
1837 26 : if(byte < -128 || byte > 255)
1838 : {
1839 : throw ed::runtime_error(
1840 2 : s.get_location()
1841 4 : + "byte values must be between -128 and +255 (position "
1842 8 : + std::to_string(i)
1843 8 : + " has out of range value "
1844 8 : + std::to_string(byte)
1845 6 : + ").");
1846 : }
1847 24 : buf.push_back(static_cast<char>(byte));
1848 26 : }
1849 2 : c->write(buf.data(), buf.size());
1850 19 : }
1851 :
1852 18 : virtual parameter_declaration const * parameter_declarations() const override
1853 : {
1854 18 : return g_send_data_params;
1855 : }
1856 :
1857 : private:
1858 : };
1859 : INSTRUCTION(send_data);
1860 :
1861 :
1862 : // SEND MESSAGE
1863 : //
1864 : class inst_send_message
1865 : : public instruction
1866 : {
1867 : public:
1868 2 : inst_send_message()
1869 6 : : instruction("send_message")
1870 : {
1871 2 : }
1872 :
1873 15 : virtual void func(state & s) override
1874 : {
1875 15 : ed::connection::vector_t v(s.get_connections());
1876 15 : if(v.empty())
1877 : {
1878 3 : throw ed::runtime_error("send_message() has no connection to send a message to.");
1879 : }
1880 : // TODO: fix the connection selection, if we have more than one,
1881 : // how do we know which one to select? (i.e. have a connection
1882 : // name included in the parameters)
1883 : //
1884 14 : ed::connection_with_send_message::pointer_t c(std::dynamic_pointer_cast<ed::connection_with_send_message>(v[0]));
1885 14 : if(c == nullptr)
1886 : {
1887 : throw ed::runtime_error("send_message() called without a valid listener connection."); // LCOV_EXCL_LINE
1888 : }
1889 :
1890 14 : ed::message msg;
1891 :
1892 42 : variable::pointer_t param(s.get_parameter("sent_server"));
1893 14 : if(param != nullptr)
1894 : {
1895 10 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1896 10 : msg.set_sent_from_server(var->get_string());
1897 10 : }
1898 :
1899 42 : param = s.get_parameter("sent_service");
1900 14 : if(param != nullptr)
1901 : {
1902 10 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1903 10 : msg.set_sent_from_service(var->get_string());
1904 10 : }
1905 :
1906 42 : param = s.get_parameter("server");
1907 14 : if(param != nullptr)
1908 : {
1909 10 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1910 10 : msg.set_server(var->get_string());
1911 10 : }
1912 :
1913 42 : param = s.get_parameter("service");
1914 14 : if(param != nullptr)
1915 : {
1916 10 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1917 10 : msg.set_service(var->get_string());
1918 10 : }
1919 :
1920 42 : param = s.get_parameter("command", true);
1921 : {
1922 14 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
1923 14 : msg.set_command(var->get_string());
1924 14 : }
1925 : //std::cerr << "--- send message [" << msg.get_command() << "]\n";
1926 :
1927 42 : param = s.get_parameter("parameters");
1928 14 : if(param != nullptr)
1929 : {
1930 10 : variable_list::pointer_t list(std::static_pointer_cast<variable_list>(param));
1931 10 : std::size_t const max(list->get_item_size());
1932 21 : for(std::size_t idx(0); idx < max; ++idx)
1933 : {
1934 12 : variable::pointer_t var(list->get_item(idx));
1935 12 : std::string const & name(var->get_name());
1936 12 : std::string const & type(var->get_type());
1937 12 : if(type == "integer")
1938 : {
1939 2 : variable_integer::pointer_t int_var(std::static_pointer_cast<variable_integer>(var));
1940 2 : msg.add_parameter(name, int_var->get_integer());
1941 2 : }
1942 10 : else if(type == "string" || type == "identifier")
1943 : {
1944 8 : variable_string::pointer_t str_var(std::static_pointer_cast<variable_string>(var));
1945 8 : msg.add_parameter(name, str_var->get_string());
1946 8 : }
1947 2 : else if(type == "timestamp")
1948 : {
1949 1 : variable_timestamp::pointer_t ts_var(std::static_pointer_cast<variable_timestamp>(var));
1950 1 : msg.add_parameter(name, ts_var->get_timestamp());
1951 1 : }
1952 : else
1953 : {
1954 : throw ed::runtime_error(
1955 : "message parameter type \""
1956 2 : + type
1957 3 : + "\" not supported yet.");
1958 : }
1959 12 : }
1960 10 : }
1961 :
1962 13 : c->send_message(msg);
1963 31 : }
1964 :
1965 114 : virtual parameter_declaration const * parameter_declarations() const override
1966 : {
1967 114 : return g_send_message_params;
1968 : }
1969 :
1970 : private:
1971 : };
1972 : INSTRUCTION(send_message);
1973 :
1974 :
1975 : // SET VARIABLE
1976 : //
1977 : class inst_set_variable
1978 : : public instruction
1979 : {
1980 : public:
1981 2 : inst_set_variable()
1982 6 : : instruction("set_variable")
1983 : {
1984 2 : }
1985 :
1986 206 : virtual void func(state & s) override
1987 : {
1988 618 : variable::pointer_t name(s.get_parameter("name", true));
1989 618 : variable::pointer_t value(s.get_parameter("value", true));
1990 :
1991 206 : std::string cast;
1992 618 : variable::pointer_t type(s.get_parameter("type"));
1993 206 : if(type != nullptr)
1994 : {
1995 7 : cast = std::static_pointer_cast<variable_string>(type)->get_string();
1996 : }
1997 :
1998 206 : std::string const var_name(std::static_pointer_cast<variable_string>(name)->get_string());
1999 206 : variable::pointer_t var(value->clone(var_name));
2000 206 : if(!cast.empty())
2001 : {
2002 7 : bool converted(true);
2003 7 : std::string const & var_type(var->get_type());
2004 7 : if(var_type == "string")
2005 : {
2006 4 : variable_string::pointer_t var_string(std::static_pointer_cast<variable_string>(var));
2007 4 : if(cast == "string")
2008 : {
2009 : ; // do nothing
2010 : }
2011 3 : else if(cast == "timestamp") // string -> timestamp
2012 : {
2013 : // expect the variable value to represent a valid
2014 : // floating point value
2015 : //
2016 2 : double timestamp(0.0);
2017 2 : if(!advgetopt::validator_double::convert_string(var_string->get_string(), timestamp))
2018 : {
2019 : throw ed::runtime_error(
2020 : "invalid timestamp, a valid floating point was expected ("
2021 2 : + var_string->get_string()
2022 3 : + ").");
2023 : }
2024 1 : variable_timestamp::pointer_t timestamp_var(std::make_shared<variable_timestamp>(var_name));
2025 1 : timestamp_var->set_timestamp(timestamp);
2026 1 : var = timestamp_var;
2027 1 : }
2028 : else
2029 : {
2030 1 : converted = false;
2031 : }
2032 4 : }
2033 3 : else if(var_type == "timestamp")
2034 : {
2035 2 : if(cast == "timestamp")
2036 : {
2037 : ; // do nothing
2038 : }
2039 : else
2040 : {
2041 1 : converted = false;
2042 : }
2043 : }
2044 : else
2045 : {
2046 1 : converted = false;
2047 : }
2048 6 : if(!converted)
2049 : {
2050 : throw ed::runtime_error(
2051 : "casting from \""
2052 6 : + var_type
2053 12 : + "\" to \""
2054 12 : + cast
2055 9 : + "\" is not yet implemented.");
2056 : }
2057 : }
2058 202 : s.set_variable(var);
2059 428 : }
2060 :
2061 1029 : virtual parameter_declaration const * parameter_declarations() const override
2062 : {
2063 1029 : return g_set_variable_params;
2064 : }
2065 :
2066 : private:
2067 : };
2068 : INSTRUCTION(set_variable);
2069 :
2070 :
2071 : // SHOW DATA
2072 : //
2073 : class inst_show_data
2074 : : public instruction
2075 : {
2076 : public:
2077 2 : inst_show_data()
2078 6 : : instruction("show_data")
2079 : {
2080 2 : }
2081 :
2082 13 : virtual void func(state & s) override
2083 : {
2084 39 : variable::pointer_t size_var(s.get_parameter("size", true));
2085 13 : variable_integer::pointer_t int_size(std::dynamic_pointer_cast<variable_integer>(size_var));
2086 13 : std::size_t const size(int_size->get_integer());
2087 13 : connection_data_t buffer;
2088 13 : ssize_t const r(s.peek_data(buffer, size));
2089 13 : if(r < 0)
2090 : {
2091 : // LCOV_EXCL_START
2092 : int const e(errno);
2093 : std::cerr
2094 : << "error: show_data() failed with "
2095 : << strerror(e)
2096 : << ".\n";
2097 : throw ed::runtime_error("show_data() failed.");
2098 : // LCOV_EXCL_STOP
2099 : }
2100 13 : if(r == 0)
2101 : {
2102 5 : std::cout << "--- data: <empty>" << std::endl;
2103 : }
2104 : else
2105 : {
2106 8 : snapdev::safe_stream save_stdout(std::cout);
2107 :
2108 8 : std::cout << "--- data:\n" << std::hex << std::setfill('0');
2109 17 : for(std::size_t i(0); i < static_cast<std::size_t>(r); i += 16)
2110 : {
2111 : std::cout
2112 : << " "
2113 9 : << std::setw(4) << i
2114 9 : << ": ";
2115 9 : std::size_t bytes(std::min(static_cast<int>(r - i), 16));
2116 9 : std::size_t j(0);
2117 61 : for(; j < bytes; ++j)
2118 : {
2119 52 : std::cout << ' ' << std::setw(2) << static_cast<int>(buffer[i + j]);
2120 : }
2121 9 : if(j < 16)
2122 : {
2123 24 : std::cout << std::string((16 - j) * 3, ' ');
2124 : }
2125 9 : std::cout << " ";
2126 61 : for(j = 0; j < bytes; ++j)
2127 : {
2128 52 : if(buffer[i + j] >= ' '
2129 52 : && buffer[i + j] <= '~')
2130 : {
2131 37 : std::cout << static_cast<char>(buffer[i + j]);
2132 : }
2133 : else
2134 : {
2135 15 : std::cout << '.';
2136 : }
2137 : }
2138 9 : std::cout << '\n';
2139 : }
2140 8 : }
2141 26 : }
2142 :
2143 39 : virtual parameter_declaration const * parameter_declarations() const override
2144 : {
2145 39 : return g_show_data_params;
2146 : }
2147 :
2148 : private:
2149 : };
2150 : INSTRUCTION(show_data);
2151 :
2152 :
2153 : // SHOW MESSAGE
2154 : //
2155 : class inst_show_message
2156 : : public instruction
2157 : {
2158 : public:
2159 2 : inst_show_message()
2160 6 : : instruction("show_message")
2161 : {
2162 2 : }
2163 :
2164 29 : virtual void func(state & s) override
2165 : {
2166 29 : ed::message const msg(s.get_message());
2167 : std::cout
2168 : << "--- message: "
2169 29 : << msg
2170 29 : << std::endl;
2171 58 : }
2172 :
2173 : private:
2174 : };
2175 : INSTRUCTION(show_message);
2176 :
2177 :
2178 : // SLEEP
2179 : //
2180 : class inst_sleep
2181 : : public instruction
2182 : {
2183 : public:
2184 2 : inst_sleep()
2185 6 : : instruction("sleep")
2186 : {
2187 2 : }
2188 :
2189 4 : virtual void func(state & s) override
2190 : {
2191 4 : snapdev::timespec_ex pause_duration;
2192 12 : variable::pointer_t seconds(s.get_parameter("seconds", true));
2193 4 : variable_integer::pointer_t int_seconds(std::dynamic_pointer_cast<variable_integer>(seconds));
2194 4 : if(int_seconds == nullptr)
2195 : {
2196 1 : variable_floating_point::pointer_t flt_seconds(std::dynamic_pointer_cast<variable_floating_point>(seconds));
2197 1 : pause_duration.set(flt_seconds->get_floating_point());
2198 1 : }
2199 : else
2200 : {
2201 3 : pause_duration.set(int_seconds->get_integer(), 0);
2202 : }
2203 4 : if(nanosleep(&pause_duration, nullptr) != 0)
2204 : {
2205 : // LCOV_EXCL_START
2206 : int const e(errno);
2207 : std::cerr
2208 : << "error: nanosleep() failed with "
2209 : << strerror(e)
2210 : << ".\n";
2211 : throw ed::runtime_error("nanosleep failed.");
2212 : // LCOV_EXCL_STOP
2213 : }
2214 8 : }
2215 :
2216 12 : virtual parameter_declaration const * parameter_declarations() const override
2217 : {
2218 12 : return g_sleep_params;
2219 : }
2220 :
2221 : private:
2222 : };
2223 : INSTRUCTION(sleep);
2224 :
2225 :
2226 : // SORT
2227 : //
2228 : class inst_sort
2229 : : public instruction
2230 : {
2231 : public:
2232 2 : inst_sort()
2233 6 : : instruction("sort")
2234 : {
2235 2 : }
2236 :
2237 6 : virtual void func(state & s) override
2238 : {
2239 : // check for variable names (var1: name1, var2: name2, ...)
2240 : //
2241 6 : variable::vector_t array;
2242 6 : std::string result_type;
2243 6 : for(int i(1);; ++i)
2244 : {
2245 66 : std::string var_number("var");
2246 22 : var_number += std::to_string(i);
2247 22 : variable::pointer_t param(s.get_parameter(var_number, false));
2248 22 : if(param == nullptr)
2249 : {
2250 3 : break;
2251 : }
2252 19 : variable_string::pointer_t var_string(std::static_pointer_cast<variable_string>(param));
2253 19 : variable::pointer_t var(s.get_variable(var_string->get_string()));
2254 19 : if(var == nullptr)
2255 : {
2256 : throw ed::runtime_error(
2257 1 : s.get_location()
2258 2 : + "variable named \""
2259 4 : + var_string->get_string()
2260 3 : + "\" not found.");
2261 : }
2262 18 : std::string const & type(var->get_type());
2263 18 : if(result_type.empty())
2264 : {
2265 5 : if(type != "string"
2266 3 : && type != "integer"
2267 8 : && type != "floating_point")
2268 : {
2269 : throw ed::runtime_error(
2270 1 : s.get_location()
2271 2 : + "sort only supports strings, integers, or floating points.");
2272 : }
2273 4 : result_type = type;
2274 : }
2275 13 : else if(type != result_type)
2276 : {
2277 : throw ed::runtime_error(
2278 1 : s.get_location()
2279 2 : + "sort only supports one type of data (\""
2280 4 : + result_type
2281 4 : + "\" in this case) for all the specified variables. \""
2282 4 : + type
2283 3 : + "\" is not compatible.");
2284 : }
2285 16 : array.push_back(var);
2286 50 : }
2287 :
2288 3 : if(result_type == "string")
2289 : {
2290 1 : std::map<std::string, bool> values;
2291 1 : std::for_each(
2292 : array.begin()
2293 : , array.end()
2294 5 : , [&values](variable::pointer_t a)
2295 : {
2296 5 : variable_string::pointer_t sa(std::dynamic_pointer_cast<variable_string>(a));
2297 5 : values[sa->get_string()] = true;
2298 10 : });
2299 1 : auto it(values.begin());
2300 1 : std::for_each(
2301 : array.begin()
2302 : , array.end()
2303 5 : , [&values, &it](variable::pointer_t a)
2304 : {
2305 5 : variable_string::pointer_t sa(std::dynamic_pointer_cast<variable_string>(a));
2306 5 : sa->set_string(it->first);
2307 5 : ++it;
2308 10 : });
2309 1 : }
2310 2 : else if(result_type == "integer")
2311 : {
2312 1 : std::map<std::int64_t, bool> values;
2313 1 : std::for_each(
2314 : array.begin()
2315 : , array.end()
2316 5 : , [&values](variable::pointer_t a)
2317 : {
2318 5 : variable_integer::pointer_t sa(std::dynamic_pointer_cast<variable_integer>(a));
2319 5 : values[sa->get_integer()] = true;
2320 10 : });
2321 1 : auto it(values.begin());
2322 1 : std::for_each(
2323 : array.begin()
2324 : , array.end()
2325 5 : , [&values, &it](variable::pointer_t a)
2326 : {
2327 5 : variable_integer::pointer_t sa(std::dynamic_pointer_cast<variable_integer>(a));
2328 5 : sa->set_integer(it->first);
2329 5 : ++it;
2330 10 : });
2331 1 : }
2332 1 : else if(result_type == "floating_point")
2333 : {
2334 1 : std::map<double, bool> values;
2335 1 : std::for_each(
2336 : array.begin()
2337 : , array.end()
2338 5 : , [&values](variable::pointer_t a)
2339 : {
2340 5 : variable_floating_point::pointer_t sa(std::dynamic_pointer_cast<variable_floating_point>(a));
2341 5 : values[sa->get_floating_point()] = true;
2342 10 : });
2343 1 : auto it(values.begin());
2344 1 : std::for_each(
2345 : array.begin()
2346 : , array.end()
2347 5 : , [&values, &it](variable::pointer_t a)
2348 : {
2349 5 : variable_floating_point::pointer_t sa(std::dynamic_pointer_cast<variable_floating_point>(a));
2350 5 : sa->set_floating_point(it->first);
2351 5 : ++it;
2352 10 : });
2353 1 : }
2354 12 : }
2355 :
2356 38 : virtual parameter_declaration const * parameter_declarations() const override
2357 : {
2358 38 : return g_sort_params;
2359 : }
2360 :
2361 : private:
2362 : };
2363 : INSTRUCTION(sort);
2364 :
2365 :
2366 : // STRLEN
2367 : //
2368 : class inst_strlen
2369 : : public instruction
2370 : {
2371 : public:
2372 2 : inst_strlen()
2373 6 : : instruction("strlen")
2374 : {
2375 2 : }
2376 :
2377 1 : virtual void func(state & s) override
2378 : {
2379 3 : variable::pointer_t st(s.get_parameter("string", true));
2380 1 : variable_string::pointer_t str(std::dynamic_pointer_cast<variable_string>(st));
2381 :
2382 3 : variable::pointer_t var_name(s.get_parameter("variable_name", true));
2383 1 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(var_name));
2384 1 : std::string const & variable_name(var->get_string());
2385 :
2386 1 : variable_integer::pointer_t new_var(std::make_shared<variable_integer>(variable_name));
2387 1 : new_var->set_integer(str->get_string().length());
2388 1 : s.set_variable(new_var);
2389 2 : }
2390 :
2391 4 : virtual parameter_declaration const * parameter_declarations() const override
2392 : {
2393 4 : return g_strlen_params;
2394 : }
2395 :
2396 : private:
2397 : };
2398 : INSTRUCTION(strlen);
2399 :
2400 :
2401 : // UNSET VARIABLE
2402 : //
2403 : class inst_unset_variable
2404 : : public instruction
2405 : {
2406 : public:
2407 2 : inst_unset_variable()
2408 6 : : instruction("unset_variable")
2409 : {
2410 2 : }
2411 :
2412 1 : virtual void func(state & s) override
2413 : {
2414 3 : variable::pointer_t name(s.get_parameter("name", true));
2415 1 : std::string const var_name(std::static_pointer_cast<variable_string>(name)->get_string());
2416 1 : s.unset_variable(var_name);
2417 2 : }
2418 :
2419 7 : virtual parameter_declaration const * parameter_declarations() const override
2420 : {
2421 7 : return g_unset_variable_params;
2422 : }
2423 :
2424 : private:
2425 : };
2426 : INSTRUCTION(unset_variable);
2427 :
2428 :
2429 : // VERIFY DATA
2430 : //
2431 : class inst_verify_data
2432 : : public instruction
2433 : {
2434 : public:
2435 2 : inst_verify_data()
2436 6 : : instruction("verify_data")
2437 : {
2438 2 : }
2439 :
2440 8 : virtual void func(state & s) override
2441 : {
2442 24 : variable::pointer_t param(s.get_parameter("values", true));
2443 8 : variable_array::pointer_t var(std::static_pointer_cast<variable_array>(param));
2444 8 : std::size_t const size(var->get_item_size());
2445 8 : if(size == 0)
2446 : {
2447 : // at this point, there is no reason for us to "send" an empty buffer
2448 : // since that would do absolutely nothing
2449 : //
2450 : throw ed::runtime_error(
2451 1 : s.get_location()
2452 2 : + "array cannot be empty.");
2453 : }
2454 :
2455 : // now that we have a size, we can read that many bytes from the
2456 : // data buffer
2457 : //
2458 7 : connection_data_t buf;
2459 7 : ssize_t const r(s.read_data(buf, size));
2460 7 : if(r != static_cast<ssize_t>(size))
2461 : {
2462 : throw ed::runtime_error(
2463 1 : s.get_location()
2464 2 : + "could not read "
2465 4 : + std::to_string(size)
2466 4 : + " bytes from the data buffer, got "
2467 4 : + std::to_string(r)
2468 3 : + " instead.");
2469 : }
2470 :
2471 46 : for(std::size_t i(0); i < size; ++i)
2472 : {
2473 41 : variable_integer::pointer_t value(std::static_pointer_cast<variable_integer>(var->get_item(i)));
2474 41 : if(buf[i] != value->get_integer())
2475 : {
2476 : throw ed::runtime_error(
2477 1 : s.get_location()
2478 2 : + "values at offset "
2479 4 : + std::to_string(i)
2480 4 : + " do not match ("
2481 4 : + std::to_string(static_cast<int>(buf[i]))
2482 4 : + " != "
2483 4 : + std::to_string(value->get_integer())
2484 3 : + ").");
2485 : }
2486 41 : }
2487 18 : }
2488 :
2489 24 : virtual parameter_declaration const * parameter_declarations() const override
2490 : {
2491 24 : return g_verify_data_params;
2492 : }
2493 :
2494 : private:
2495 : };
2496 : INSTRUCTION(verify_data);
2497 :
2498 :
2499 : // VERIFY MESSAGE
2500 : //
2501 : class inst_verify_message
2502 : : public instruction
2503 : {
2504 : public:
2505 2 : inst_verify_message()
2506 6 : : instruction("verify_message")
2507 : {
2508 2 : }
2509 :
2510 30 : virtual void func(state & s) override
2511 : {
2512 30 : ed::message const msg(s.get_message());
2513 :
2514 91 : check_value(s, "sent_server", msg.get_sent_from_server());
2515 88 : check_value(s, "sent_service", msg.get_sent_from_service());
2516 85 : check_value(s, "server", msg.get_server());
2517 82 : check_value(s, "service", msg.get_service());
2518 81 : check_value(s, "command", msg.get_command());
2519 :
2520 76 : check_parameters(s, msg, "required_parameters", false, false);
2521 48 : check_parameters(s, msg, "optional_parameters", true, false);
2522 49 : check_parameters(s, msg, "forbidden_parameters", false, true);
2523 45 : }
2524 :
2525 140 : void check_value(
2526 : state & s
2527 : , std::string const name
2528 : , std::string const & value)
2529 : {
2530 140 : variable::pointer_t param(s.get_parameter(name));
2531 140 : if(param == nullptr)
2532 : {
2533 106 : return;
2534 : }
2535 :
2536 34 : std::string const & type(param->get_type());
2537 34 : if(type == "string" || type == "identifier")
2538 : {
2539 31 : variable_string::pointer_t var(std::static_pointer_cast<variable_string>(param));
2540 31 : if(var->get_string() != value)
2541 : {
2542 : throw ed::runtime_error(
2543 5 : s.get_location()
2544 10 : + "message expected \""
2545 20 : + name
2546 20 : + "\", set to \""
2547 20 : + value
2548 20 : + "\", to match \""
2549 20 : + var->get_string()
2550 15 : + "\".");
2551 : }
2552 31 : }
2553 3 : else if(type == "regex")
2554 : {
2555 2 : variable_regex::pointer_t regex_var(std::static_pointer_cast<variable_regex>(param));
2556 2 : std::regex const compiled_regex(regex_var->get_regex());
2557 2 : if(!std::regex_match(value, compiled_regex))
2558 : {
2559 : throw ed::runtime_error(
2560 1 : s.get_location()
2561 2 : + "message expected \""
2562 4 : + name
2563 4 : + "\", set to \""
2564 4 : + value
2565 4 : + "\", to match regex \""
2566 4 : + regex_var->get_regex()
2567 3 : + "\".");
2568 : }
2569 3 : }
2570 : else
2571 : {
2572 : throw ed::runtime_error(
2573 1 : s.get_location()
2574 2 : + "message value \""
2575 4 : + name
2576 4 : + "\" does not support type \""
2577 4 : + type
2578 3 : + "\".");
2579 : }
2580 140 : }
2581 :
2582 55 : void check_parameters(
2583 : state & s
2584 : , ed::message const & msg
2585 : , std::string const & list_name
2586 : , bool optional
2587 : , bool forbidden)
2588 : {
2589 55 : variable::pointer_t param(s.get_parameter(list_name));
2590 55 : if(param == nullptr)
2591 : {
2592 14 : return;
2593 : }
2594 :
2595 41 : variable_list::pointer_t list(std::static_pointer_cast<variable_list>(param));
2596 41 : std::size_t const max(list->get_item_size());
2597 87 : for(std::size_t idx(0); idx < max; ++idx)
2598 : {
2599 54 : variable::pointer_t var(list->get_item(idx));
2600 54 : std::string const & name(var->get_name());
2601 54 : if(msg.has_parameter(name))
2602 : {
2603 35 : if(forbidden)
2604 : {
2605 : throw ed::runtime_error(
2606 1 : s.get_location()
2607 2 : + "message forbidden parameter \""
2608 4 : + name
2609 3 : + "\" was found in this message.");
2610 : }
2611 : }
2612 19 : else if(optional || forbidden)
2613 : {
2614 18 : continue;
2615 : }
2616 : else // if(required)
2617 : {
2618 : throw ed::runtime_error(
2619 1 : s.get_location()
2620 2 : + "message required parameter \""
2621 4 : + name
2622 3 : + "\" was not found in this message.");
2623 : }
2624 :
2625 34 : std::string const & type(var->get_type());
2626 34 : if(type == "integer")
2627 : {
2628 12 : std::int64_t const value(msg.get_integer_parameter(name));
2629 12 : variable_integer::pointer_t int_var(std::static_pointer_cast<variable_integer>(var));
2630 12 : if(int_var->get_integer() != value)
2631 : {
2632 : throw ed::runtime_error(
2633 1 : s.get_location()
2634 2 : + "message expected parameter \""
2635 4 : + name
2636 4 : + "\" to be an integer set to \""
2637 4 : + std::to_string(int_var->get_integer())
2638 4 : + "\" but found \""
2639 4 : + std::to_string(value)
2640 3 : + "\" instead.");
2641 : }
2642 12 : }
2643 22 : else if(type == "string" || type == "identifier")
2644 : {
2645 16 : std::string value(msg.get_parameter(name));
2646 16 : variable_string::pointer_t str_var(std::static_pointer_cast<variable_string>(var));
2647 16 : if(str_var->get_string() != value)
2648 : {
2649 : // if the strings are really long, remove everything
2650 : // that's considered equal so we can better see
2651 : // what is not and quickly act on it
2652 : //
2653 2 : std::string expected(str_var->get_string());
2654 2 : if(expected.length() > 100
2655 2 : || value.length() > 100)
2656 : {
2657 1 : bool erased(false);
2658 10 : while(!expected.empty()
2659 10 : && !value.empty()
2660 20 : && expected[0] == value[0])
2661 : {
2662 9 : expected.erase(0, 1);
2663 9 : value.erase(0, 1);
2664 9 : erased = true;
2665 : }
2666 1 : if(erased)
2667 : {
2668 1 : expected = "..." + expected;
2669 1 : value = "..." + value;
2670 : }
2671 : }
2672 : throw ed::runtime_error(
2673 2 : s.get_location()
2674 4 : + "message expected parameter \""
2675 8 : + name
2676 8 : + "\" to be a string set to \""
2677 8 : + expected
2678 8 : + "\" but found \""
2679 8 : + value
2680 6 : + "\" instead.");
2681 2 : }
2682 18 : }
2683 6 : else if(type == "regex")
2684 : {
2685 3 : std::string const value(msg.get_parameter(name));
2686 3 : variable_regex::pointer_t regex_var(std::static_pointer_cast<variable_regex>(var));
2687 3 : std::regex const compiled_regex(regex_var->get_regex());
2688 3 : if(!std::regex_match(value, compiled_regex))
2689 : {
2690 : throw ed::runtime_error(
2691 1 : s.get_location()
2692 2 : + "message expected parameter \""
2693 4 : + name
2694 4 : + "\", set to \""
2695 4 : + value
2696 4 : + "\", to match regex \""
2697 4 : + regex_var->get_regex()
2698 3 : + "\".");
2699 : }
2700 5 : }
2701 3 : else if(type == "timestamp")
2702 : {
2703 2 : snapdev::timespec_ex const value(msg.get_timespec_parameter(name));
2704 2 : variable_timestamp::pointer_t timestamp_var(std::static_pointer_cast<variable_timestamp>(var));
2705 2 : if(timestamp_var->get_timestamp() != value)
2706 : {
2707 : throw ed::runtime_error(
2708 1 : s.get_location()
2709 2 : + "message expected parameter \""
2710 4 : + name
2711 4 : + "\", set to \""
2712 4 : + value.to_string()
2713 4 : + "\", to match timestamp \""
2714 4 : + timestamp_var->get_timestamp().to_string()
2715 3 : + "\".");
2716 : }
2717 2 : }
2718 1 : else if(type == "void")
2719 : {
2720 : // we already checked that the parameter exists
2721 : // we don't need to check the value since all values
2722 : // match "void"
2723 : ;
2724 : }
2725 : else
2726 : {
2727 : throw ed::runtime_error(
2728 1 : s.get_location()
2729 2 : + "message parameter type \""
2730 4 : + type
2731 3 : + "\" not supported yet.");
2732 : }
2733 54 : }
2734 63 : }
2735 :
2736 165 : virtual parameter_declaration const * parameter_declarations() const override
2737 : {
2738 165 : return g_verify_message_params;
2739 : }
2740 :
2741 : private:
2742 : };
2743 : INSTRUCTION(verify_message);
2744 :
2745 :
2746 : // WAIT
2747 : //
2748 : class inst_wait
2749 : : public instruction
2750 : {
2751 : public:
2752 : enum class mode_t
2753 : {
2754 : MODE_WAIT, // cannot timeout and it must have connections (default)
2755 : MODE_DRAIN, // empty list of connections expected
2756 : MODE_TIMEOUT, // timeout expected
2757 : };
2758 :
2759 2 : inst_wait()
2760 6 : : instruction("wait")
2761 : {
2762 2 : }
2763 :
2764 93 : virtual void func(state & s) override
2765 : {
2766 93 : if(!s.get_in_thread())
2767 : {
2768 3 : throw ed::runtime_error("wait() used before run().");
2769 : }
2770 :
2771 92 : snapdev::timespec_ex timeout_duration;
2772 276 : variable::pointer_t timeout(s.get_parameter("timeout", true));
2773 92 : variable_integer::pointer_t int_seconds(std::dynamic_pointer_cast<variable_integer>(timeout));
2774 92 : if(int_seconds == nullptr)
2775 : {
2776 32 : variable_floating_point::pointer_t flt_seconds(std::dynamic_pointer_cast<variable_floating_point>(timeout));
2777 32 : timeout_duration.set(flt_seconds->get_floating_point());
2778 32 : }
2779 : else
2780 : {
2781 60 : timeout_duration.set(int_seconds->get_integer(), 0);
2782 : }
2783 :
2784 92 : mode_t mode(mode_t::MODE_WAIT);
2785 276 : variable::pointer_t mode_param(s.get_parameter("mode"));
2786 92 : if(mode_param != nullptr)
2787 : {
2788 88 : variable_string::pointer_t mode_name(std::static_pointer_cast<variable_string>(mode_param));
2789 88 : std::string const & m(mode_name->get_string());
2790 88 : if(m == "wait")
2791 : {
2792 79 : mode = mode_t::MODE_WAIT;
2793 : }
2794 9 : else if(m == "drain")
2795 : {
2796 7 : mode = mode_t::MODE_DRAIN;
2797 : }
2798 2 : else if(m == "timeout")
2799 : {
2800 1 : mode = mode_t::MODE_TIMEOUT;
2801 : }
2802 : else
2803 : {
2804 : throw ed::runtime_error(
2805 1 : s.get_location()
2806 2 : + "unknown mode \""
2807 4 : + m
2808 3 : + "\" in wait().");
2809 : }
2810 88 : }
2811 :
2812 : for(;;)
2813 : {
2814 96 : int const r(poll(s, timeout_duration, mode));
2815 93 : if(r == 0)
2816 : {
2817 8 : if(mode == mode_t::MODE_DRAIN)
2818 : {
2819 7 : break;
2820 : }
2821 3 : throw ed::runtime_error("no connections to wait() on.");
2822 : }
2823 85 : if(mode != mode_t::MODE_DRAIN)
2824 : {
2825 80 : break;
2826 : }
2827 5 : }
2828 189 : }
2829 :
2830 96 : int poll(state & s, snapdev::timespec_ex timeout_duration, mode_t mode)
2831 : {
2832 96 : std::vector<struct pollfd> fds;
2833 96 : std::map<ed::connection *, int> position;
2834 96 : ed::connection::vector_t connections(s.get_connections());
2835 96 : ed::connection::pointer_t listen(s.get_listen_connection());
2836 96 : if(listen != nullptr)
2837 : {
2838 95 : connections.push_back(listen);
2839 : }
2840 251 : for(auto & c : connections)
2841 : {
2842 155 : int e(0);
2843 155 : if(mode != mode_t::MODE_DRAIN)
2844 : {
2845 131 : if(c->is_listener() || c->is_signal())
2846 : {
2847 83 : e |= POLLIN;
2848 : }
2849 131 : if(c->is_reader())
2850 : {
2851 48 : e |= POLLIN | POLLPRI | POLLRDHUP;
2852 : }
2853 : }
2854 155 : if(c->is_writer())
2855 : {
2856 9 : e |= POLLOUT | POLLRDHUP;
2857 : }
2858 155 : if(e == 0)
2859 : {
2860 19 : continue;
2861 : }
2862 :
2863 136 : position[c.get()] = fds.size();
2864 136 : struct pollfd fd;
2865 136 : fd.fd = c->get_socket();
2866 136 : fd.events = e;
2867 136 : fd.revents = 0;
2868 136 : fds.push_back(fd);
2869 : }
2870 96 : if(fds.empty())
2871 : {
2872 : // if draining, this means "DONE" otherwise it's an error
2873 : //
2874 8 : return 0;
2875 : }
2876 :
2877 : for(;;)
2878 : {
2879 88 : int const r(ppoll(&fds[0], fds.size(), &timeout_duration, nullptr));
2880 88 : if(r < 0)
2881 : {
2882 : // LCOV_EXCL_START
2883 : int const e(errno);
2884 : if(e == EINTR)
2885 : {
2886 : std::cerr
2887 : << "error: got an interrupt while ppoll() in reporter. Trying again.\n";
2888 : continue;
2889 : }
2890 : throw ed::runtime_error(
2891 : "ppoll() returned an error: "
2892 : + std::to_string(e)
2893 : + ", "
2894 : + strerror(e));
2895 : // LCOV_EXCL_STOP
2896 : }
2897 88 : break;
2898 : } // LCOV_EXCL_LINE
2899 88 : bool timed_out(true);
2900 229 : for(auto & c : connections)
2901 : {
2902 141 : struct pollfd const * fd(&fds[position[c.get()]]);
2903 141 : if(fd->revents != 0)
2904 : {
2905 89 : timed_out = false;
2906 :
2907 : // an event happened on this one
2908 : //
2909 89 : if((fd->revents & (POLLIN | POLLPRI)) != 0)
2910 : {
2911 : // we consider that Unix signals have the greater priority
2912 : // and thus handle them first
2913 : //
2914 75 : if(c->is_signal())
2915 : {
2916 : // LCOV_EXCL_START
2917 : ed::signal * ss(dynamic_cast<ed::signal *>(c.get()));
2918 : if(ss != nullptr)
2919 : {
2920 : ss->process();
2921 : }
2922 : // LCOV_EXCL_STOP
2923 : }
2924 75 : else if(c->is_listener())
2925 : {
2926 : // a listener is a special case and we want
2927 : // to call process_accept() instead
2928 : //
2929 35 : c->process_accept();
2930 : }
2931 : else
2932 : {
2933 40 : c->process_read();
2934 : }
2935 : }
2936 89 : if((fd->revents & POLLOUT) != 0)
2937 : {
2938 14 : c->process_write();
2939 : }
2940 89 : if((fd->revents & POLLERR) != 0)
2941 : {
2942 : c->process_error(); // LCOV_EXCL_LINE
2943 : }
2944 89 : if((fd->revents & (POLLHUP | POLLRDHUP)) != 0)
2945 : {
2946 3 : c->process_hup();
2947 : }
2948 89 : if((fd->revents & POLLNVAL) != 0)
2949 : {
2950 : c->process_invalid(); // LCOV_EXCL_LINE
2951 : }
2952 : }
2953 : }
2954 88 : if(timed_out && mode != mode_t::MODE_TIMEOUT)
2955 : {
2956 : // if we wake up without any event then we have a timeout
2957 : //
2958 : // TBD: we may need to call the process_timeout() on some
2959 : // connections? At this point I don't see why the
2960 : // server side would need such...
2961 : //
2962 9 : throw ed::runtime_error("ppoll() timed out.");
2963 : }
2964 :
2965 85 : return fds.size();
2966 105 : }
2967 :
2968 261 : virtual parameter_declaration const * parameter_declarations() const override
2969 : {
2970 261 : return g_wait_params;
2971 : }
2972 : };
2973 : INSTRUCTION(wait);
2974 :
2975 :
2976 :
2977 : } // namespace reporter
2978 : } // namespace SNAP_CATCH2_NAMESPACE
2979 : // vim: ts=4 sw=4 et
|