root/maint/mocked/based.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mock_based_ipc_accept
  2. mock_based_ipc_closed
  3. mock_based_ipc_destroy
  4. mock_based_handle_query
  5. mock_based_common_callback_worker
  6. mock_based_dispatch_command
  7. mock_based_register_module
  8. mock_based_options
  9. main

   1 /*
   2  * Copyright 2019-2020 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * Licensed under the GNU General Public License version 2 or later (GPLv2+).
   7  */
   8 
   9 /*
  10  * Clean room attempt (admittedly with lot of code borrowed or inspired from
  11  * the full-blown daemon), minimalistic implementation of based daemon, with
  12  * only important aspects being implemented at the moment.
  13  *
  14  * Hopefully easy to adapt for variety of purposes.
  15  *
  16  * NOTE: currently, only cib_rw API end-point is opened, future refinements
  17  *       as new modules are added should conditionalize per what the module
  18  *       indicates in the context (which is intentionally very loose data glue
  19  *       between the skeleton and modules themselves (like CGI variables so
  20  *       to say, but more structurally predestined so as to avoid complexities
  21  *       of hash table lookups etc.)
  22  */
  23 
  24 #include <crm_internal.h>
  25 #if 0
  26 #include "crm/common/ipc_internal.h"  /* pcmk__client_t */
  27 #include "crm/common/xml.h"  /* crm_xml_add */
  28 #endif
  29 #include "crm/msg_xml.h"  /* F_SUBTYPE */
  30 #include "daemons/based/pacemaker-based.h"  /* cib_notify_diff */
  31 
  32 #include <qb/qbipcs.h>  /* qb_ipcs_connection_t */
  33 
  34 #include "based.h"
  35 
  36 
  37 /* direct global access violated in one case only
  38    - mock_based_ipc_accept adds a reference to it to crm_cient_t->userdata */
  39 mock_based_context_t mock_based_context;
  40 
  41 
  42 /* see based/based_callbacks.c:cib_ipc_accept */
  43 static int32_t
  44 mock_based_ipc_accept(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
  45 {
  46     int32_t ret = 0;
  47     pcmk__client_t *cib_client;
  48 
  49     crm_trace("Connection %p", c);
  50     cib_client = pcmk__new_client(c, uid, gid);
  51     if (cib_client == NULL) {
  52         ret = -EIO;
  53     }
  54 
  55     cib_client->userdata = &mock_based_context;
  56 
  57     return ret;
  58 }
  59 
  60 /* see based/based_callbacks.c:cib_ipc_closed */
  61 static int32_t
  62 mock_based_ipc_closed(qb_ipcs_connection_t *c)
     /* [previous][next][first][last][top][bottom][index][help] */
  63 {
  64     pcmk__client_t *client = pcmk__find_client(c);
  65 
  66     if (client != NULL) {
  67         crm_trace("Connection %p", c);
  68         pcmk__free_client(client);
  69     }
  70 
  71     return 0;
  72 }
  73 
  74 /* see based/based_callbacks.c:cib_ipc_destroy */
  75 static void
  76 mock_based_ipc_destroy(qb_ipcs_connection_t *c)
     /* [previous][next][first][last][top][bottom][index][help] */
  77 {
  78     crm_trace("Connection %p", c);
  79     mock_based_ipc_closed(c);
  80 }
  81 
  82 /* see based/based_callbacks.c:cib_process_command (and more) */
  83 static void
  84 mock_based_handle_query(pcmk__client_t *cib_client, uint32_t flags,
     /* [previous][next][first][last][top][bottom][index][help] */
  85                         const xmlNode *op_request)
  86 {
  87     xmlNode *reply, *cib;
  88     const char cib_str[] =
  89 #if 0
  90 "<cib/>";
  91 #else
  92 "<cib validate-with='pacemaker-1.2' admin_epoch='0' epoch='0' num_updates='0'>"\
  93 "  <configuration>"\
  94 "    <crm_config/>"\
  95 "    <nodes/>"\
  96 "    <resources/>"\
  97 "    <constraints/>"\
  98 "  </configuration>"\
  99 "  <status/>"\
 100 "</cib>";
 101 #endif
 102     cib = xmlReadMemory(cib_str, sizeof(cib_str), "file:///tmp/foo", NULL, 0)->children;
 103 
 104     reply = create_xml_node(NULL, "cib-reply");
 105     crm_xml_add(reply, F_TYPE, T_CIB);
 106     crm_xml_add(reply, F_CIB_OPERATION,
 107                 crm_element_value(op_request, F_CIB_OPERATION));
 108     crm_xml_add(reply, F_CIB_CALLID,
 109                 crm_element_value(op_request, F_CIB_CALLID));
 110     crm_xml_add(reply, F_CIB_CLIENTID,
 111                 crm_element_value(op_request, F_CIB_CLIENTID));
 112     crm_xml_add_int(reply, F_CIB_CALLOPTS, flags);
 113     crm_xml_add_int(reply, F_CIB_RC, pcmk_ok);
 114 
 115     if (cib != NULL) {
 116         crm_trace("Attaching reply output");
 117         add_message_xml(reply, F_CIB_CALLDATA, cib);
 118     }
 119 
 120     pcmk__ipc_send_xml(cib_client, cib_client->request_id, reply,
 121                        ((flags & cib_sync_call)? crm_ipc_flags_none
 122                         : crm_ipc_server_event));
 123 
 124     free_xml(reply);
 125     free_xml(cib);
 126 }
 127 
 128 /* see based/based_callbacks.c:cib_common_callback_worker */
 129 static void
 130 mock_based_common_callback_worker(uint32_t id, uint32_t flags,
     /* [previous][next][first][last][top][bottom][index][help] */
 131                                   xmlNode *op_request,
 132                                   pcmk__client_t *cib_client)
 133 {
 134     const char *op = crm_element_value(op_request, F_CIB_OPERATION);
 135     mock_based_context_t *ctxt;
 136 
 137     if (!strcmp(op, CRM_OP_REGISTER)) {
 138         if (flags & crm_ipc_client_response) {
 139             xmlNode *ack = create_xml_node(NULL, __func__);
 140             crm_xml_add(ack, F_CIB_OPERATION, CRM_OP_REGISTER);
 141             crm_xml_add(ack, F_CIB_CLIENTID, cib_client->id);
 142             pcmk__ipc_send_xml(cib_client, id, ack, flags);
 143             cib_client->request_id = 0;
 144             free_xml(ack);
 145         }
 146 
 147     } else if (!strcmp(op, T_CIB_NOTIFY)) {
 148         int on_off = 0;
 149         const char *type = crm_element_value(op_request, F_CIB_NOTIFY_TYPE);
 150         crm_element_value_int(op_request, F_CIB_NOTIFY_ACTIVATE, &on_off);
 151 
 152         crm_debug("Setting %s callbacks for %s (%s): %s",
 153                   type, cib_client->name, cib_client->id, on_off ? "on" : "off");
 154 
 155         if (!strcmp(type, T_CIB_DIFF_NOTIFY) && on_off) {
 156             pcmk__set_client_flags(cib_client, cib_notify_diff);
 157         }
 158 
 159         ctxt = (mock_based_context_t *) cib_client->userdata;
 160         for (size_t c = ctxt->modules_cnt; c > 0; c--) {
 161             if (ctxt->modules[c - 1]->hooks.cib_notify != NULL) {
 162                 ctxt->modules[c - 1]->hooks.cib_notify(cib_client);
 163             }
 164         }
 165 
 166         if (flags & crm_ipc_client_response) {
 167             pcmk__ipc_send_ack(cib_client, id, flags, "ack", CRM_EX_OK);
 168         }
 169 
 170     } else if (!strcmp(op, CIB_OP_QUERY)) {
 171         mock_based_handle_query(cib_client, flags, op_request);
 172 
 173     } else {
 174         crm_notice("Discarded request %s", op);
 175     }
 176 }
 177 
 178 /* see based/based_callbacks.c:cib_ipc_dispatch_rw */
 179 static int32_t
 180 mock_based_dispatch_command(qb_ipcs_connection_t *c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 181 {
 182     uint32_t id = 0, flags = 0;
 183     int call_options = 0;
 184     pcmk__client_t *cib_client = pcmk__find_client(c);
 185     xmlNode *op_request = pcmk__client_data2xml(cib_client, data, &id, &flags);
 186 
 187     crm_notice("Got connection %p", c);
 188     assert(op_request != NULL);
 189 
 190     if (cib_client == NULL || op_request == NULL) {
 191         if (op_request == NULL) {
 192             crm_trace("Invalid message from %p", c);
 193             pcmk__ipc_send_ack(cib_client, id, flags, "nack", CRM_EX_PROTOCOL);
 194         }
 195         return 0;
 196     }
 197 
 198     crm_element_value_int(op_request, F_CIB_CALLOPTS, &call_options);
 199     if (call_options & cib_sync_call) {
 200         assert(flags & crm_ipc_client_response);
 201         cib_client->request_id = id;  /* reply only to last in-flight request */
 202     }
 203 
 204     assert(cib_client->name == NULL);
 205     crm_element_value_int(op_request, F_CIB_CALLOPTS, &call_options);
 206     crm_xml_add(op_request, F_CIB_CLIENTID, cib_client->id);
 207     crm_xml_add(op_request, F_CIB_CLIENTNAME, cib_client->name);
 208 
 209     mock_based_common_callback_worker(id, flags, op_request, cib_client);
 210     free_xml(op_request);
 211 
 212     return 0;
 213 }
 214 
 215 /* * */
 216 
 217 size_t mock_based_register_module(module_t mod) {
     /* [previous][next][first][last][top][bottom][index][help] */
 218     module_t *module;
 219     size_t ret = mock_based_context.modules_cnt++;
 220 
 221     mock_based_context.modules = realloc(mock_based_context.modules,
 222                                          sizeof(*mock_based_context.modules)
 223                                           * mock_based_context.modules_cnt);
 224     if (mock_based_context.modules == NULL
 225             || (module = malloc(sizeof(module_t))) == NULL) {
 226         abort();
 227     }
 228 
 229     memcpy(module, &mod, sizeof(mod));
 230     mock_based_context.modules[mock_based_context.modules_cnt - 1] = module;
 231 
 232     return ret;
 233 }
 234 
 235 static int
 236 mock_based_options(mock_based_context_t *ctxt,
     /* [previous][next][first][last][top][bottom][index][help] */
 237                    bool usage, int argc, const char *argv[])
 238 {
 239     const char **args2argv;
 240     char *s;
 241     int ret = 0;
 242 
 243     if (argc <= 1) {
 244         const char *help_argv[] = {argv[0], "-h"};
 245         return mock_based_options(ctxt, false, 2, (const char **) &help_argv);
 246     }
 247 
 248     for (size_t i = 1; i < argc; i++) {
 249         if (argv[i][0] == '-' && argv[i][1] != '-' && argv[i][1] != '\0') {
 250             if (usage) {
 251                 printf("\t-%c\t", argv[i][1]);
 252             }
 253             switch(argv[i][1]) {
 254             case 'h':
 255                 if (usage) {
 256                     printf("show this help message\n");
 257                     ret = 1;
 258 
 259                 } else {
 260                     if ((args2argv
 261                             = malloc((ctxt->modules_cnt + 2) * sizeof(*args2argv))) == NULL
 262                         || (s
 263                             = malloc((ctxt->modules_cnt * 2 + 2) * sizeof(*s))) == NULL) {
 264                         return -1;
 265                     }
 266                     s[0] = 'h';
 267                     args2argv[ctxt->modules_cnt + 1] = (char[]){'-', 'h', '\0'};
 268                     for (size_t c = ctxt->modules_cnt; c > 0; c--) {
 269                         args2argv[c] = (char[]){'-', ctxt->modules[c - 1]->shortopt, '\0'};
 270                         s[(ctxt->modules_cnt - i) + 1] = '|';
 271                         s[(ctxt->modules_cnt - i) + 2] = ctxt->modules[c - 1]->shortopt;
 272                     }
 273                     s[ctxt->modules_cnt * 2 + 1] = '\0';
 274                     printf("Usage: %s [-{%s}]\n", argv[0], s);
 275                     (void) mock_based_options(ctxt, true, 2 + ctxt->modules_cnt, args2argv);
 276                     free(args2argv);
 277                     free(s);
 278                 }
 279                 return ret;
 280             default:
 281                 for (size_t c = ctxt->modules_cnt; c > 0; c--) {
 282                     if (ctxt->modules[c - 1]->shortopt == argv[i][1]) {
 283                         ret = ctxt->modules[c - 1]->hooks.argparse(ctxt, usage, argc - i, &argv[i]);
 284                         if (ret < 0) {
 285                             break;
 286                         } else if (ret > 1) {
 287                             i += (ret - 1);
 288                         }
 289                     }
 290                 }
 291                 if (ret == 0) {
 292                     printf("uknown option \"%s\"\n", argv[i]);
 293                 }
 294                 break;
 295             }
 296         }
 297     }
 298     return ret;
 299 }
 300 
 301 int main(int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help] */
 302 {
 303     mock_based_context_t *ctxt = &mock_based_context;
 304 
 305     if (mock_based_options(ctxt, false, argc, (const char **) argv) > 0) {
 306         struct qb_ipcs_service_handlers cib_ipc_callbacks = {
 307             .connection_accept = mock_based_ipc_accept,
 308             .connection_created = NULL,
 309             .msg_process = mock_based_dispatch_command,
 310             .connection_closed = mock_based_ipc_closed,
 311             .connection_destroyed = mock_based_ipc_destroy,
 312         };
 313         crm_log_preinit(NULL, argc, argv);
 314         crm_log_init(NULL, LOG_DEBUG, false, true, argc, argv, false);
 315         qb_ipcs_service_t *ipcs_command =
 316             mainloop_add_ipc_server(PCMK__SERVER_BASED_RW, QB_IPC_NATIVE,
 317                                     &cib_ipc_callbacks);
 318         g_main_loop_run(g_main_loop_new(NULL, false));
 319         qb_ipcs_destroy(ipcs_command);
 320     }
 321 
 322     for (size_t c = ctxt->modules_cnt; c > 0; c--) {
 323         if (ctxt->modules[c - 1]->hooks.destroy != NULL) {
 324             ctxt->modules[c - 1]->hooks.destroy(ctxt->modules[c - 1]);
 325         }
 326         free(mock_based_context.modules[c - 1]);
 327     }
 328 
 329     free(mock_based_context.modules);
 330 }

/* [previous][next][first][last][top][bottom][index][help] */