]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
metaplugin: Extend query interface for unstructured data.
authorRadosław Korzeniewski <radoslaw@korzeniewski.net>
Tue, 29 Jun 2021 15:22:59 +0000 (17:22 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 24 Mar 2022 08:03:03 +0000 (09:03 +0100)
Now you can use a `Data` packet to respond with any data for
queryParameter() API. This data will be simply forwarded to
Bacula without scanning and interpretation by metaplugin.

bacula/src/plugins/fd/pluginlib/metaplugin.cpp
bacula/src/plugins/fd/pluginlib/ptcomm.cpp
bacula/src/plugins/fd/pluginlib/ptcomm.h
bacula/src/plugins/fd/pluginlib/test_metaplugin_backend.c
regress/scripts/metaplugin-protocol-tests.sh

index ce15c27d305ddeab23ee051e1645273652bb64ee..bc4629540a0a6e15422cfd9a9ec31b1171e15b43 100644 (file)
@@ -2437,13 +2437,6 @@ bRC METAPLUGIN::handleXACLdata(bpContext *ctx, struct xacl_pkt *xacl)
  */
 bRC METAPLUGIN::queryParameter(bpContext *ctx, struct query_pkt *qp)
 {
-   bRC ret = bRC_More;
-   OutputWriter ow(qp->api_opts);
-   POOL_MEM cmd(PM_MESSAGE);
-   char *p, *q, *t;
-   alist values(10, not_owned_by_alist);
-   key_pair *kp;
-
    DMSG0(ctx, D1, "METAPLUGIN::queryParameter\n");
 
    // check if it is our Plugin command
@@ -2460,6 +2453,8 @@ bRC METAPLUGIN::queryParameter(bpContext *ctx, struct query_pkt *qp)
       }
    }
 
+   POOL_MEM cmd(PM_MESSAGE);
+
    if (listing == None) {
       listing = Query;
       Mmsg(cmd, "%s query=%s", qp->command, qp->parameter);
@@ -2469,12 +2464,16 @@ bRC METAPLUGIN::queryParameter(bpContext *ctx, struct query_pkt *qp)
    }
 
    /* read backend response */
-   if (backend.ctx->read_command(ctx, cmd) < 0){
+   char pkt = 0;
+   int32_t pktlen = backend.ctx->read_any(ctx, &pkt, cmd);
+   if (pktlen < 0) {
       DMSG(ctx, DERROR, "Cannot read backend query response for %s command.\n", qp->parameter);
       JMSG(ctx, backend.ctx->jmsg_err_level(), "Cannot read backend query response for %s command.\n", qp->parameter);
       return bRC_Error;
    }
 
+   bRC ret = bRC_More;
+
    /* check EOD */
    if (backend.ctx->is_eod()){
       /* got EOD so the backend finish response, so terminate the chat */
@@ -2484,48 +2483,72 @@ bRC METAPLUGIN::queryParameter(bpContext *ctx, struct query_pkt *qp)
       qp->result = NULL;
       ret = bRC_OK;
    } else {
-      /*
-      * here we have:
-      *    key=value[,key2=value2[,...]]
-      * parameters we should decompose
-      */
-      p = cmd.c_str();
-      while (*p != '\0'){
-         q = strchr(p, ',');
-         if (q != NULL){
-            *q++ = '\0';
-         }
-         // single key=value
-         DMSG(ctx, D1, "METAPLUGIN::queryParameter:scan %s\n", p);
-         if ((t = strchr(p, '=')) != NULL){
-            *t++ = '\0';
-         } else {
-            t = (char*)"";     // pointer to empty string
-         }
-         DMSG2(ctx, D1, "METAPLUGIN::queryParameter:pair '%s' = '%s'\n", p, t);
-         if (strlen(p) > 0){
-            // push values only when we have key name
-            kp = New(key_pair(p, t));
-            values.append(kp);
-         }
-         p = q != NULL ? q : (char*)"";
-      }
+      switch (pkt)
+      {
+      case 'C':
+         {
+            OutputWriter ow(qp->api_opts);
+            char *p, *q, *t;
+            alist values(10, not_owned_by_alist);
+            key_pair *kp;
+
+            /*
+            * here we have:
+            *    key=value[,key2=value2[,...]]
+            * parameters we should decompose
+            */
+            p = cmd.c_str();
+            while (*p != '\0') {
+               q = strchr(p, ',');
+               if (q != NULL) {
+                  *q++ = '\0';
+               }
+               // single key=value
+               DMSG(ctx, D1, "METAPLUGIN::queryParameter:scan %s\n", p);
+               if ((t = strchr(p, '=')) != NULL) {
+                  *t++ = '\0';
+               } else {
+                  t = (char*)"";     // pointer to empty string
+               }
+               DMSG2(ctx, D1, "METAPLUGIN::queryParameter:pair '%s' = '%s'\n", p, t);
+               if (strlen(p) > 0) {
+                  // push values only when we have key name
+                  kp = New(key_pair(p, t));
+                  values.append(kp);
+               }
+               p = q != NULL ? q : (char*)"";
+            }
 
-      // if more values then one then it is a list
-      if (values.size() > 1){
-         DMSG0(ctx, D1, "METAPLUGIN::queryParameter: will render list\n")
-         ow.start_list(qp->parameter);
-      }
-      // render all values
-      foreach_alist(kp, &values){
-         ow.get_output(OT_STRING, kp->key.c_str(), kp->value.c_str(), OT_END);
-         delete kp;
-      }
-      if (values.size() > 1){
-         ow.end_list();
+            // if more values then one then it is a list
+            if (values.size() > 1) {
+               DMSG0(ctx, D1, "METAPLUGIN::queryParameter: will render list\n")
+               ow.start_list(qp->parameter);
+            }
+            // render all values
+            foreach_alist(kp, &values) {
+               ow.get_output(OT_STRING, kp->key.c_str(), kp->value.c_str(), OT_END);
+               delete kp;
+            }
+            if (values.size() > 1) {
+               ow.end_list();
+            }
+            pm_strcpy(robjbuf, ow.get_output(OT_END));
+            qp->result = robjbuf.c_str();
+         }
+         break;
+      case 'D':
+         pm_memcpy(robjbuf, cmd.c_str(), pktlen);
+         qp->result = robjbuf.c_str();
+         break;
+      default:
+         DMSG(ctx, DERROR, "METAPLUGIN::queryParameter: got invalid packet: %c\n", pkt);
+         JMSG(ctx, M_ERROR, "METAPLUGIN::queryParameter: got invalid packet: %c\n", pkt);
+         backend.ctx->signal_term(ctx);
+         backend.ctx->terminate(ctx);
+         qp->result = NULL;
+         ret = bRC_Error;
+         break;
       }
-      pm_strcpy(robjbuf, ow.get_output(OT_END));
-      qp->result = robjbuf.c_str();
    }
 
    return ret;
index 4c5cfce896bc0c703479e4fff60a9feff9c8f218..2d83d183e842cb7a5b5eefcaf5b8bbb221a1059f 100644 (file)
@@ -282,14 +282,20 @@ bool PTCOMM::sendbackend_data(bpContext *ctx, POOLMEM *buf, int32_t nbytes)
  * @param cmd - an expected command to read: `C` or `D`
  * @return int32_t - the size of the packet payload
  */
-int32_t PTCOMM::recvbackend_header(bpContext *ctx, char cmd)
+int32_t PTCOMM::recvbackend_header(bpContext *ctx, char *cmd, bool any)
 {
-   if (is_closed()){
+   if (is_closed()) {
       DMSG0(ctx, DERROR, "BPIPE to backend is closed, cannot receive data.\n");
       JMSG0(ctx, is_fatal() ? M_FATAL : M_ERROR, "BPIPE to backend is closed, cannot receive data.\n");
       return -1;
    }
 
+   if (cmd == NULL) {
+      DMSG0(ctx, DERROR, "Runtime error. cmd == NULL. Cannot read data.\n");
+      JMSG0(ctx, is_fatal() ? M_FATAL : M_ERROR, "Runtime error. cmd == NULL. Cannot read data.\n");
+      return -1;
+   }
+
    PTHEADER header;
    bool workdone = false;
 
@@ -326,15 +332,16 @@ int32_t PTCOMM::recvbackend_header(bpContext *ctx, char cmd)
       // convert packet length from ASCII to binary
       int32_t msglen = atoi(header.length);
 
-      if (header.status == 'C' || header.status == 'D')
-      {
-         if (header.status != cmd)
-         {
-            DMSG2(ctx, DERROR, "Protocol error. Expected packet: %c got: %c\n", cmd, header.status);
-            JMSG2(ctx, M_FATAL, "Protocol error. Expected packet: %c got: %c\n", cmd, header.status);
-            return -1;
+      if (header.status == 'C' || header.status == 'D') {
+         if (!any) {
+            if (header.status != *cmd) {
+               DMSG2(ctx, DERROR, "Protocol error. Expected packet: %c got: %c\n", *cmd, header.status);
+               JMSG2(ctx, M_FATAL, "Protocol error. Expected packet: %c got: %c\n", *cmd, header.status);
+               return -1;
+            }
+         } else {
+            *cmd = header.status;
          }
-
          // this means no additional handling required
          return msglen;
       }
@@ -440,14 +447,13 @@ int32_t PTCOMM::recvbackend_header(bpContext *ctx, char cmd)
  * @param cmd
  * @return int32_t
  */
-int32_t PTCOMM::handle_read_header(bpContext *ctx, char cmd)
+int32_t PTCOMM::handle_read_header(bpContext *ctx, char *cmd, bool any)
 {
    // first read is the packet header where we will have info about data
    // which is sent to us; the packet header is 8 chars/bytes length fixed
    // nbytes shows how many bytes we expects to read
-   int32_t length = recvbackend_header(ctx, cmd);
-   if (length < 0)
-   {
+   int32_t length = recvbackend_header(ctx, cmd, any);
+   if (length < 0) {
       // error
       DMSG0(ctx, DERROR, "PTCOMM cannot get packet header from backend.\n");
       JMSG0(ctx, is_fatal() ? M_FATAL : M_ERROR, "PTCOMM cannot get packet header from backend.\n");
@@ -502,16 +508,16 @@ int32_t PTCOMM::handle_payload(bpContext *ctx, char *buf, int32_t nbytes)
  *        ctx is not NULL
  *    <n>: the size of received message
  */
-int32_t PTCOMM::recvbackend(bpContext *ctx, char cmd, POOL_MEM &buf)
+int32_t PTCOMM::recvbackend(bpContext *ctx, char *cmd, POOL_MEM &buf, bool any)
 {
    // handle header
-   int32_t length = handle_read_header(ctx, cmd);
-   if (length < 0)
+   int32_t length = handle_read_header(ctx, cmd, any);
+   if (length < 0) {
       return -1;
+   }
 
    // handle data payload
-   if (length > 0)
-   {
+   if (length > 0) {
       // check requested buffer size
       buf.check_size(length + 1);
       return handle_payload(ctx, buf.c_str(), length);
@@ -541,16 +547,17 @@ int32_t PTCOMM::recvbackend(bpContext *ctx, char cmd, POOL_MEM &buf)
 int32_t PTCOMM::recvbackend_fixed(bpContext *ctx, char cmd, char *buf, int32_t bufsize)
 {
    int32_t length = remaininglen;
+   char lcmd = cmd;
 
-   if (!f_cont){
+   if (!f_cont) {
       // handle header
-      length = handle_read_header(ctx, cmd);
+      length = handle_read_header(ctx, &lcmd);
       if (length < 0)
          return -1;
    }
 
    // handle data payload
-   if (length > 0){
+   if (length > 0) {
       // we will need subsequent call to handle remaining data only when `buf` to short
       f_cont = length > bufsize;
       int32_t nbytes = f_cont * bufsize + (!f_cont) * length;
@@ -666,10 +673,11 @@ int32_t PTCOMM::sendbackend(bpContext *ctx, char cmd, POOLMEM *buf, int32_t len)
  */
 int32_t PTCOMM::read_command(bpContext *ctx, POOL_MEM &buf)
 {
-   int32_t status = recvbackend(ctx, 'C', buf);
-   if (status > 0)
-   {
+   char cmd = 'C';
+   int32_t status = recvbackend(ctx, &cmd, buf, false);
+   if (status > 0) {
       /* mark end of string because every command is a string */
+      buf.check_size(status + 1);
       buf.c_str()[status] = '\0';
       /* strip any junk in command like '\n' or trailing spaces */
       strip_trailing_junk(buf.c_str());
@@ -678,6 +686,34 @@ int32_t PTCOMM::read_command(bpContext *ctx, POOL_MEM &buf)
    return status;
 }
 
+/**
+ * @brief Reads the next packet from the backend communication channel.
+ *
+ * It accepts any command or data packet. The next byte after
+ * the received data will be terminated with '\0' for easy string
+ * handling.
+ *
+ * @param ctx bpContext - for Bacula debug and jobinfo messages
+ * @param cmd a pointer to a `char` which show what kind of packet was received
+ * @param buf buffer allocated for command
+ * @return int32_t
+ *    -1 - when encountered any error
+ *    0 - when backend sent signal, i.e. EOD or Term
+ *    <n> - the number of bytes received, success
+ */
+int32_t PTCOMM::read_any(bpContext *ctx, char *cmd, POOL_MEM &buf)
+{
+   int32_t status = recvbackend(ctx, cmd, buf, true);
+   if (status > 0) {
+      /* mark end of string for easy usage */
+      buf.check_size(status + 1);
+      buf.c_str()[status] = '\0';
+      status++;
+   }
+
+   return status;
+}
+
 /*
  * Reads the next data message from the backend.
  *    The number of bytes received will not exceed the buffer length even when
@@ -697,11 +733,12 @@ int32_t PTCOMM::read_command(bpContext *ctx, POOL_MEM &buf)
 int32_t PTCOMM::read_data(bpContext *ctx, POOL_MEM &buf)
 {
    int32_t status;
+   char cmd = 'D';
 
-   if (extpipe > 0){
+   if (extpipe > 0) {
       status = read(extpipe, buf.c_str(), buf.size());
    } else {
-      status = recvbackend(ctx, 'D', buf);
+      status = recvbackend(ctx, &cmd, buf, false);
    }
 
    return status;
@@ -748,9 +785,9 @@ int32_t PTCOMM::read_data_fixed(bpContext *ctx, char *buf, int32_t len)
 bool PTCOMM::read_ack(bpContext *ctx)
 {
    POOL_MEM buf(PM_FNAME);
+   char cmd = 'F';
 
-   if (recvbackend(ctx, 'F', buf) == 0 && f_eod)
-   {
+   if (recvbackend(ctx, &cmd, buf, false) == 0 && f_eod) {
       f_eod = false;
       return true;
    }
index 2eb28175fa44679031701bbc4afdab81b6909231..0176c0347fabe0229722c0facec4346c5d1c30a3 100644 (file)
@@ -89,11 +89,11 @@ protected:
    bool recvbackend_data(bpContext *ctx, char *buf, int32_t nbytes);
    bool sendbackend_data(bpContext *ctx, char *buf, int32_t nbytes);
 
-   int32_t recvbackend_header(bpContext *ctx, char cmd);
-   int32_t handle_read_header(bpContext *ctx, char cmd);
+   int32_t recvbackend_header(bpContext *ctx, char *cmd, bool any=false);
+   int32_t handle_read_header(bpContext *ctx, char *cmd, bool any=false);
    int32_t handle_payload(bpContext *ctx, char *buf, int32_t nbytes);
 
-   int32_t recvbackend(bpContext *ctx, char cmd, POOL_MEM &buf);
+   int32_t recvbackend(bpContext *ctx, char *cmd, POOL_MEM &buf, bool any=false);
    int32_t recvbackend_fixed(bpContext *ctx, char cmd, char *buf, int32_t bufsize);
 
    int32_t sendbackend(bpContext *ctx, char cmd, POOLMEM *buf, int32_t len);
@@ -124,6 +124,7 @@ public:
    bool handshake(bpContext *ctx, const char *pluginname, const char *pluginapi);
 
    int32_t read_command(bpContext *ctx, POOL_MEM &buf);
+   int32_t read_any(bpContext *ctx, char *cmd, POOL_MEM &buf);
    int32_t read_data(bpContext *ctx, POOL_MEM &buf);
    int32_t read_data_fixed(bpContext *ctx, char *buf, int32_t len);
 
index f447f4fb1b2f55ebbd29929b9a5fab382f58d67e..d6e1a3bf77aa378cca002019bca10ee537b6b927 100644 (file)
@@ -830,19 +830,80 @@ void perform_listing(char *listing){
    signal_eod();
 }
 
+const unsigned char m_json[] = {
+  0x7b, 0x22, 0x77, 0x69, 0x64, 0x67, 0x65, 0x74, 0x22, 0x3a, 0x20, 0x7b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x22, 0x64, 0x65, 0x62, 0x75, 0x67, 0x22,
+  0x3a, 0x20, 0x22, 0x6f, 0x6e, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20,
+  0x22, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x22, 0x3a, 0x20, 0x7b, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x74, 0x69, 0x74,
+  0x6c, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65,
+  0x20, 0x4b, 0x6f, 0x6e, 0x66, 0x61, 0x62, 0x75, 0x6c, 0x61, 0x74, 0x6f,
+  0x72, 0x20, 0x57, 0x69, 0x64, 0x67, 0x65, 0x74, 0x22, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65,
+  0x22, 0x3a, 0x20, 0x22, 0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x77, 0x69, 0x6e,
+  0x64, 0x6f, 0x77, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x22, 0x77, 0x69, 0x64, 0x74, 0x68, 0x22, 0x3a, 0x20, 0x35,
+  0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x22, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0x3a, 0x20, 0x35, 0x30,
+  0x30, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20, 0x20, 0x20,
+  0x20, 0x22, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x22, 0x3a, 0x20, 0x7b, 0x20,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x73, 0x72,
+  0x63, 0x22, 0x3a, 0x20, 0x22, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x73, 0x2f,
+  0x53, 0x75, 0x6e, 0x2e, 0x70, 0x6e, 0x67, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22,
+  0x3a, 0x20, 0x22, 0x73, 0x75, 0x6e, 0x31, 0x22, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x68, 0x4f, 0x66, 0x66, 0x73,
+  0x65, 0x74, 0x22, 0x3a, 0x20, 0x32, 0x35, 0x30, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x76, 0x4f, 0x66, 0x66, 0x73,
+  0x65, 0x74, 0x22, 0x3a, 0x20, 0x32, 0x35, 0x30, 0x2c, 0x0a, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x61, 0x6c, 0x69, 0x67, 0x6e,
+  0x6d, 0x65, 0x6e, 0x74, 0x22, 0x3a, 0x20, 0x22, 0x63, 0x65, 0x6e, 0x74,
+  0x65, 0x72, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x2c, 0x0a, 0x20,
+  0x20, 0x20, 0x20, 0x22, 0x74, 0x65, 0x78, 0x74, 0x22, 0x3a, 0x20, 0x7b,
+  0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x64, 0x61,
+  0x74, 0x61, 0x22, 0x3a, 0x20, 0x22, 0x43, 0x6c, 0x69, 0x63, 0x6b, 0x20,
+  0x48, 0x65, 0x72, 0x65, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x20, 0x20, 0x22, 0x73, 0x69, 0x7a, 0x65, 0x22, 0x3a, 0x20, 0x33,
+  0x36, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22,
+  0x73, 0x74, 0x79, 0x6c, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x62, 0x6f, 0x6c,
+  0x64, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x22, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3a, 0x20, 0x22, 0x74, 0x65, 0x78,
+  0x74, 0x31, 0x22, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x22, 0x68, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x20,
+  0x32, 0x35, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x22, 0x76, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x22, 0x3a, 0x20,
+  0x31, 0x30, 0x30, 0x2c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+  0x20, 0x22, 0x61, 0x6c, 0x69, 0x67, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x22,
+  0x3a, 0x20, 0x22, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x22, 0x2c, 0x0a,
+  0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x6f, 0x6e, 0x4d,
+  0x6f, 0x75, 0x73, 0x65, 0x55, 0x70, 0x22, 0x3a, 0x20, 0x22, 0x73, 0x75,
+  0x6e, 0x31, 0x2e, 0x6f, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x20, 0x3d,
+  0x20, 0x28, 0x73, 0x75, 0x6e, 0x31, 0x2e, 0x6f, 0x70, 0x61, 0x63, 0x69,
+  0x74, 0x79, 0x20, 0x2f, 0x20, 0x31, 0x30, 0x30, 0x29, 0x20, 0x2a, 0x20,
+  0x39, 0x30, 0x3b, 0x22, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x7d, 0x0a, 0x7d,
+  0x7d, 0x0a, 0x00
+};
+unsigned int m_json_len = 603;
+
 /*
  * The query param procedure
  *    return 3 simple parameters
  */
-void perform_queryparam(char *query)
+void perform_queryparam(const char *query)
 {
    /* Query Loop (5) */
-   snprintf(buf, BIGBUFLEN, "%s=test1\n", query);
-   write_plugin('C', buf);
-   snprintf(buf, BIGBUFLEN, "%s=test2\n", query);
-   write_plugin('C', buf);
-   snprintf(buf, BIGBUFLEN, "%s=test3\n", query);
-   write_plugin('C', buf);
+   if (strcmp(query, "m_id") == 0) {
+      snprintf(buf, BIGBUFLEN, "%s=test1\n", query);
+      write_plugin('C', buf);
+      snprintf(buf, BIGBUFLEN, "%s=test2\n", query);
+      write_plugin('C', buf);
+      snprintf(buf, BIGBUFLEN, "%s=test3\n", query);
+      write_plugin('C', buf);
+   } else
+   if (strcmp(query, "m_json") == 0) {
+      write_plugin_bin((const char*)m_json, m_json_len);
+      write_plugin('D', "UmFkb3PFgmF3IEtvcnplbmlld3NraQo=\n");
+   }
 
    /* this is the end of all data */
    signal_eod();
index 4925d617370551588ae9ab033192fb0f19f493fb..e91b1a7c148236a1ad987aafaf90c1bb8a8ab0b6 100755 (executable)
@@ -367,6 +367,22 @@ END_OF_DATA
 run_bconsole
 TEST=$((TEST+1))
 
+cat <<END_OF_DATA >${cwd}/tmp/bconcmds
+@#
+@# Query
+@#
+@output /dev/null
+messages
+@$out ${cwd}/tmp/qlog${TEST}.out
+setdebug level=500 client=$CLIENT trace=1
+.query client=$CLIENT plugin="$Plugin" parameter="m_json"
+messages
+@output
+quit
+END_OF_DATA
+run_bconsole
+TEST=$((TEST+1))
+
 stop_bacula
 
 RET=$(grep "jobstatus:" ${cwd}/tmp/log1.out | awk '{print $2}')
@@ -535,4 +551,12 @@ then
    estat=3
 fi
 
+JSON=$(grep -c "widget" ${cwd}/tmp/qlog3.out)
+BASE=$(grep -c "UmFkb3PFgmF3IEtvcnplbmlld3NraQo=" ${cwd}/tmp/qlog3.out)
+if [ "$JSON" -ne 1 ] || [ "$BASE" -ne 1 ]
+then
+   echo "qlog3" "$JSON" "$BASE"
+   estat=4
+fi
+
 end_test