LCOV - code coverage report
Current view: top level - eventdispatcher/reporter - basic_instructions.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 1049 1049
Test Date: 2025-05-30 15:24:13 Functions: 100.0 % 109 109
Legend: Lines: hit not hit

            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
        

Generated by: LCOV version 2.0-1

Snap C++ | List of projects | List of versions