]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add api v2 json output
authorEric Bollengier <eric@baculasystems.com>
Mon, 4 Oct 2021 16:57:37 +0000 (18:57 +0200)
committerEric Bollengier <eric@baculasystems.com>
Wed, 6 Sep 2023 07:49:01 +0000 (09:49 +0200)
bconsole
.api 2 api_opts=j
.status dir header

bacula/src/dird/ua_purge.c
bacula/src/dird/ua_status.c
bacula/src/filed/status.c
bacula/src/lib/lib.h
bacula/src/lib/output.c
bacula/src/lib/output.h
bacula/src/lib/status.h
bacula/src/stored/cloud_dev.c
bacula/src/stored/cloud_transfer_mgr.c
bacula/src/stored/status.c

index f23c14040f5425613b038406767a3e89340a2e69..44bd518e21ce8aabb43155362599f2d0119c2393 100644 (file)
@@ -802,8 +802,6 @@ int truncate_cmd(UAContext *ua, const char *cmd)
    if (find_arg(ua, "cache") > 0) {
       return cloud_volumes_cmd(ua, cmd, "truncate cache");
    }
-   
-   bmemset(&pr, 0, sizeof(pr));
 
    /*
     * Look for all Purged volumes that can be recycled, are enabled and
index 40a75867ca0a08cd9c2ed9993cfa2f50d61f4737..a8b2d865c4659c2a300d95b2bd07a23cb8bc616d 100644 (file)
@@ -440,6 +440,7 @@ static void api_list_dir_status_header(UAContext *ua)
    OutputWriter wt(ua->api_opts);
    wt.start_group("header");
    wt.get_output(
+      OT_START_OBJ,
       OT_STRING, "name",        my_name,
       OT_STRING, "version",     VERSION " (" BDATE ")",
       OT_STRING, "uname",       HOST_OS " " DISTNAME " " DISTVER,
@@ -460,6 +461,7 @@ static void api_list_dir_status_header(UAContext *ua)
       OT_INT64,  "debug",       debug_level,
       OT_INT,    "trace",       get_trace(),
       OT_ALIST_STR, "tags",     debug_get_tags_list(&tlist, debug_level_tags),
+      OT_END_OBJ,
       OT_END);
 
    ua->send_msg("%s", wt.end_group());
@@ -1237,6 +1239,10 @@ static void list_running_jobs(UAContext *ua)
          }
       }
       endeach_jcr(jcr);
+
+   } else if (ua->api > 1) {    // We always return something in APIv2
+      ow.start_group("running");
+      ow.get_output(OT_START_ARRAY, OT_END);
    }
 
    njobs = 0; /* count the number of job really displayed */
@@ -1254,9 +1260,6 @@ static void list_running_jobs(UAContext *ua)
          if (!ua->api) {
             ua->send_msg(_(" JobId  Type Level     Files     Bytes  Name              Status\n"));
             ua->send_msg(_("======================================================================\n"));
-
-         } else if (ua->api > 1) {
-            ua->send_msg(ow.start_group("running", false));
          }
       }
       status = jcr->JobStatus;
@@ -1446,28 +1449,27 @@ static void list_running_jobs(UAContext *ua)
          STORE    *w = wstore;
          STORE    *r = rstore;
          JOB      *j = jcr->job;
-         ua->send_msg("%s", ow.get_output(OT_CLEAR,
-                         OT_START_OBJ,
-                         OT_INT32,   "jobid",     jcr->JobId,
-                         OT_JOBLEVEL,"level",     jcr->getJobLevel(),
-                         OT_JOBTYPE, "type",      jcr->getJobType(),
-                         OT_JOBSTATUS,"status",   status,
-                         OT_STRING,  "status_desc",msg,
-                         OT_STRING,  "comment",   jcr->comment,
-                         OT_SIZE,    "jobbytes",  jcr->JobBytes,
-                         OT_INT32,   "jobfiles",  jcr->JobFiles,
-                         OT_STRING,  "job",       jcr->Job,
-                         OT_STRING,  "name",      j?j->name():"",
-                         OT_STRING,  "clientname",c?c->name():"",
-                         OT_STRING,  "fileset",   f?f->name():"",
-                         OT_STRING,  "storage",   w?w->name():"",
-                         OT_STRING,  "rstorage",  r?r->name():"",
-                         OT_UTIME,   "schedtime", jcr->sched_time,
-                         OT_UTIME,   "starttime", jcr->start_time,
-                         OT_INT32,   "priority",  jcr->JobPriority,
-                         OT_INT32,   "errors",    jcr->JobErrors,
-                         OT_END_OBJ,
-                         OT_END));
+         ow.get_output(OT_START_OBJ,
+                       OT_INT32,   "jobid",     jcr->JobId,
+                       OT_JOBLEVEL,"level",     jcr->getJobLevel(),
+                       OT_JOBTYPE, "type",      jcr->getJobType(),
+                       OT_JOBSTATUS,"status",   status,
+                       OT_STRING,  "status_desc",msg,
+                       OT_STRING,  "comment",   jcr->comment,
+                       OT_SIZE,    "jobbytes",  jcr->JobBytes,
+                       OT_INT32,   "jobfiles",  jcr->JobFiles,
+                       OT_STRING,  "job",       jcr->Job,
+                       OT_STRING,  "name",      j?j->name():"",
+                       OT_STRING,  "clientname",c?c->name():"",
+                       OT_STRING,  "fileset",   f?f->name():"",
+                       OT_STRING,  "storage",   w?w->name():"",
+                       OT_STRING,  "rstorage",  r?r->name():"",
+                       OT_UTIME,   "schedtime", jcr->sched_time,
+                       OT_UTIME,   "starttime", jcr->start_time,
+                       OT_INT32,   "priority",  jcr->JobPriority,
+                       OT_INT32,   "errors",    jcr->JobErrors,
+                       OT_END_OBJ,
+                       OT_END);
 
       } else {
          char b1[50], b2[50], b3[50];
@@ -1488,6 +1490,12 @@ static void list_running_jobs(UAContext *ua)
    }
    endeach_jcr(jcr);
 
+   if (ua->api > 1) {
+      ow.end_list();
+      char *p = ow.end_group();
+      ua->send_msg("%s", p);
+   }
+
    if (njobs == 0) {
       /* Note the following message is used in regress -- don't change */
       ua->send_msg(_("No Jobs running.\n====\n"));
@@ -1497,8 +1505,6 @@ static void list_running_jobs(UAContext *ua)
       /* display a closing header */
       if (!ua->api) {
          ua->send_msg("====\n");
-      } else if (ua->api > 1) {
-         ua->send_msg(ow.end_group(false));
       }
    }
    Dmsg0(200, "leave list_run_jobs()\n");
@@ -1506,12 +1512,25 @@ static void list_running_jobs(UAContext *ua)
 
 static void list_terminated_jobs(UAContext *ua)
 {
-   char dt[MAX_TIME_LENGTH], b1[30], b2[30];
+   char dt[MAX_TIME_LENGTH], b1[30], b2[30], *p;
    char level[10];
+   bool add_sep=false;
    OutputWriter ow(ua->api_opts);
 
+   if (ua->api > 1) {
+      ow.start_group("terminated");
+      p = ow.get_output(OT_START_ARRAY, OT_END);
+      ua->send_msg("%s", p);
+   }
+   
    if (last_jobs->empty()) {
-      if (!ua->api) ua->send_msg(_("No Terminated Jobs.\n"));
+      if (!ua->api) {
+         ua->send_msg(_("No Terminated Jobs.\n"));
+      } else if (ua->api > 1) {
+         ow.get_output(OT_CLEAR, OT_END_ARRAY, OT_END);
+         p = ow.end_group();
+         ua->send_msg("%s", p);
+      }
       return;
    }
    lock_last_jobs_list();
@@ -1520,8 +1539,6 @@ static void list_terminated_jobs(UAContext *ua)
       ua->send_msg(_("\nTerminated Jobs:\n"));
       ua->send_msg(_(" JobId  Level     Files      Bytes   Status   Finished        Name \n"));
       ua->send_msg(_("====================================================================\n"));
-   } else if (ua->api > 1) {
-      ua->send_msg(ow.start_group("terminated"));
    }
    foreach_dlist(je, last_jobs) {
       char JobName[MAX_NAME_LENGTH];
@@ -1591,6 +1608,7 @@ static void list_terminated_jobs(UAContext *ua)
       } else if (ua->api > 1) {
          ua->send_msg("%s",
                       ow.get_output(OT_CLEAR,
+                                    add_sep ? OT_SEP : OT_NOP,
                                     OT_START_OBJ,
                                     OT_INT32,   "jobid",     je->JobId,
                                     OT_JOBLEVEL,"level",     je->JobLevel,
@@ -1606,6 +1624,7 @@ static void list_terminated_jobs(UAContext *ua)
                                     OT_END_OBJ,
                                     OT_END));
 
+         add_sep=true;
       } else {
          ua->send_msg(_("%6d  %-7s %8s %10s  %-7s  %-8s %s\n"),
             je->JobId,
@@ -1619,7 +1638,9 @@ static void list_terminated_jobs(UAContext *ua)
    if (!ua->api) {
       ua->send_msg(_("\n"));
    } else if (ua->api > 1) {
-      ua->send_msg(ow.end_group(false));
+      ow.get_output(OT_CLEAR, OT_END_ARRAY, OT_END);
+      p = ow.end_group();
+      ua->send_msg("%s", p);
    }
    unlock_last_jobs_list();
 }
@@ -1648,12 +1669,12 @@ static void list_collectors_status(UAContext *ua)
       }
       Dmsg1(500, "processing: %s\n", res->res_collector.hdr.name);
       render_collector_status(res->res_collector, buf);
-      ua->send_msg(buf.c_str());
+      ua->send_msg("%s", buf.c_str());
    };
    UnlockRes();
    if (!collname){
       render_updcollector_status(buf);
-      ua->send_msg(buf.c_str());
+      ua->send_msg("%s", buf.c_str());
    }
    Dmsg0(200, "leave list_collectors_status()\n");
 }
@@ -1665,7 +1686,9 @@ static void api_collectors_status(UAContext *ua, char *collname)
    POOLMEM *buf;
 
    Dmsg1(200, "enter api_collectors_status() %s\n", NPRTB(collname));
-   ow.start_group("collector_backends");
+   ow.start_group("collector");
+   ow.get_output(OT_START_OBJ, OT_END);
+   ow.start_list("collector_backends");
    LockRes();
    foreach_res(res, R_COLLECTOR) {
       if (collname && !bstrcmp(collname, res->res_collector.hdr.name)){
@@ -1675,12 +1698,13 @@ static void api_collectors_status(UAContext *ua, char *collname)
       api_render_collector_status(res->res_collector, ow);
    };
    UnlockRes();
-   buf = ow.end_group();
+   ow.end_list();
    if (!collname){
-      ow.start_group("collector_update");
+      ow.get_output(OT_SEP, OT_LABEL, "collector_update", OT_END);
       api_render_updcollector_status(ow);
-      buf = ow.end_group();
    }
-   ua->send_msg(buf);
+   ow.get_output(OT_END_OBJ, OT_END);
+   buf = ow.end_group();
+   ua->send_msg("%s", buf);
    Dmsg0(200, "leave api_collectors_status()\n");
 };
index 38b32b6dda8e97d8c8458d372ac742a0ba5b7b63..7b30cef8b44f0008f786c8acdbc717b93be12b33 100644 (file)
@@ -103,9 +103,9 @@ static void api_list_status_header(STATUS_PKT *sp)
          }
       }
    }
-   
    wt.start_group("header");
    wt.get_output(
+      OT_START_OBJ,
       OT_STRING, "name",        my_name,
       OT_STRING, "version",     VERSION " (" BDATE ")",
       OT_STRING, "uname",       HOST_OS " " DISTNAME " " DISTVER,
@@ -121,11 +121,12 @@ static void api_list_status_header(STATUS_PKT *sp)
       OT_PLUGINS, "plugins",    b_plugin_list,
       OT_INT,     "pkiencryption", (int)me->pki_encrypt,
       OT_INT,     "pkisignature", (int)me->pki_sign,
-      OT_STRING,     "pkicipher",  NPRTB(cipher),
-      OT_STRING,     "pkidigest",  NPRTB(digest),
+      OT_STRING,  "pkicipher",  NPRTB(cipher),
+      OT_STRING,  "pkidigest",  NPRTB(digest),
       OT_INT32,   "fips",       crypto_get_fips(),
       OT_STRING,  "crypto",     crypto_get_version(),
       OT_BOOL,    "gpfs",       GPFSLIB::enabled(),
+      OT_END_OBJ,
       OT_END);
    p = wt.end_group();
    sendit(p, strlen(p), sp);
@@ -399,10 +400,14 @@ static void  list_running_jobs_api(STATUS_PKT *sp)
 {
    OutputWriter ow(sp->api_opts);
    int sec, bps, brps, val;
+   bool add_sep=false;
    char *p;
    JCR *njcr;
 
    /* API v1, edit with comma, space before the name, sometime ' ' as separator */
+   ow.start_group("running");
+   p = ow.get_output(OT_START_ARRAY, OT_END);
+   sendit(p, strlen(p), sp);
 
    foreach_jcr(njcr) {
       int vss = 0;
@@ -411,12 +416,19 @@ static void  list_running_jobs_api(STATUS_PKT *sp)
          vss = 1;
       }
 #endif
-      p = ow.get_output(OT_CLEAR, OT_START_OBJ, OT_END);
+      if (add_sep) {
+         p = ow.get_output(OT_CLEAR, OT_SEP, OT_START_OBJ, OT_END);
+
+      } else {
+         p = ow.get_output(OT_CLEAR, OT_START_OBJ, OT_END);
+         add_sep = true;
+      }
 
       if (njcr->JobId == 0) {
          val = (njcr->dir_bsock && njcr->dir_bsock->tls)?1:0;
          ow.get_output(OT_UTIME, "DirectorConnected", (utime_t)njcr->start_time,
                        OT_INT, "DirTLS", val,
+                       OT_END_OBJ,
                        OT_END);
       } else {
          ow.get_output(OT_INT32,   "JobId", njcr->JobId,
@@ -429,10 +441,12 @@ static void  list_running_jobs_api(STATUS_PKT *sp)
                        OT_END);
 
       }
+
       sendit(p, strlen(p), sp);
       if (njcr->JobId == 0) {
          continue;
       }
+
       sec = time(NULL) - njcr->start_time;
       if (sec <= 0) {
          sec = 1;
@@ -440,6 +454,7 @@ static void  list_running_jobs_api(STATUS_PKT *sp)
       bps = (int)(njcr->JobBytes / sec);
       brps = (int)(njcr->ReadBytes / sec);
       ow.get_output(OT_CLEAR,
+                    OT_SEP,     // We have started to display info
                     OT_INT32,   "JobFiles",  njcr->JobFiles,
                     OT_SIZE,    "JobBytes",  njcr->JobBytes,
                     OT_INT,     "Bytes/sec", bps,
@@ -458,7 +473,7 @@ static void  list_running_jobs_api(STATUS_PKT *sp)
       }
 
       sendit(p, strlen(p), sp);
-      ow.get_output(OT_CLEAR, OT_END);
+      ow.get_output(OT_CLEAR, OT_SEP, OT_END);
 
       if (njcr->JobFiles > 0) {
          njcr->lock();
@@ -483,6 +498,10 @@ static void  list_running_jobs_api(STATUS_PKT *sp)
       sendit(p, strlen(p), sp);
    }
    endeach_jcr(njcr);
+
+   ow.get_output(OT_CLEAR, OT_END_ARRAY, OT_END);
+   p = ow.end_group();
+   sendit(p, strlen(p), sp);
 }
 
 static void  list_running_jobs(STATUS_PKT *sp)
@@ -631,7 +650,9 @@ static void api_collectors_status(STATUS_PKT *sp, char *collname)
    POOLMEM *buf;
 
    Dmsg1(200, "enter api_collectors_status() %s\n", NPRTB(collname));
-   ow.start_group("collector_backends");
+   ow.start_group("collector");
+   ow.get_output(OT_START_OBJ, OT_END);
+   ow.start_list("collector_backends");
    LockRes();
    foreach_res(res, R_COLLECTOR) {
       if (collname && !bstrcmp(collname, res->res_collector.hdr.name)){
@@ -641,12 +662,13 @@ static void api_collectors_status(STATUS_PKT *sp, char *collname)
       api_render_collector_status(res->res_collector, ow);
    };
    UnlockRes();
-   buf = ow.end_group();
+   ow.end_list();
    if (!collname){
-      ow.start_group("collector_update");
+      ow.get_output(OT_SEP, OT_LABEL, "collector_update", OT_END);
       api_render_updcollector_status(ow);
-      buf = ow.end_group();
    }
+   ow.get_output(OT_END_OBJ, OT_END);
+   buf = ow.end_group();
    sendit(buf, strlen(buf), sp);
    Dmsg0(200, "leave api_collectors_status()\n");
 };
index 14425c38fe4d17901812623ab72cf34f4af14c8b..c7df548f25d1850fb7a7afa803bff07b11edec54 100644 (file)
@@ -68,8 +68,8 @@
 #include "guid_to_name.h"
 #include "htable.h"
 #include "sellist.h"
-#include "output.h"
 #include "protos.h"
+#include "output.h"
 #include "bget_msg.h"
 #if BEEF
 #include "bee_lib_dedup.h"
index 10a75f3710ceb328a83170ea51d18bf84ad84395..d703a44d0dd8872b9b6db567a7a6636bb01a8a80 100644 (file)
@@ -26,6 +26,8 @@
 
 /* use new output (lowercase, no special char) */
 #define OF_USE_NEW_OUTPUT  1
+#define OF_USE_QUOTES      2
+#define OF_USE_JSON        4
 
 void OutputWriter::parse_options(const char *options)
 {
@@ -38,10 +40,31 @@ void OutputWriter::parse_options(const char *options)
       nb=0;
 
       switch(*p) {
+      case 'q':
+         flags |= OF_USE_QUOTES;
+         break;
+
+      case 'j':
+         flags |= OF_USE_QUOTES; // q
+         flags |= OF_USE_JSON;   // j
+         flags |= OF_USE_NEW_OUTPUT; /* o lowercase and only isalpha */
+         set_separator(',');         // S44
+         set_equal(':');             // e123
+         set_label(":");             // l
+         set_hash("{", "}");
+         set_table("[", "]");
+         set_object_separator('{', '}');
+         break;
+
       case 'C':
          flags = 0;
          set_time_format(OW_DEFAULT_TIMEFORMAT);
          set_separator(OW_DEFAULT_SEPARATOR);
+         set_equal(OW_DEFAULT_EQUAL);
+         set_hash(OW_DEFAULT_OPEN_HASH, OW_DEFAULT_CLOSE_HASH);
+         set_table(OW_DEFAULT_OPEN_TABLE, OW_DEFAULT_CLOSE_TABLE);
+         set_label(OW_DEFAULT_LABEL);
+         set_object_separator(0, 0);
          break;
 
       case 'S':                 /* object separator */
@@ -49,7 +72,7 @@ void OutputWriter::parse_options(const char *options)
             nb = nb*10 + (*(++p) - '0');
          }
          if (isascii(nb)) {
-            set_object_separator((char) nb);
+            set_object_separator((char) nb, (char)nb);
          }
          break;
 
@@ -72,6 +95,25 @@ void OutputWriter::parse_options(const char *options)
             set_separator((char) nb);
          }
          break;
+      case 'e':
+         while(isdigit(*(p+1))) {
+            nb = nb*10 + (*(++p) - '0');
+         }
+         if (isascii(nb)) {
+            set_equal((char) nb);
+         }
+         break;
+      case 'l':
+         while(isdigit(*(p+1))) {
+            nb = nb*10 + (*(++p) - '0');
+         }
+         if (isascii(nb)) {
+            char ed1[2];
+            ed1[0] = (char) nb;
+            ed1[1] = 0;
+            set_label(ed1);
+         }
+         break;
       default:
          break;
       }
@@ -79,23 +121,57 @@ void OutputWriter::parse_options(const char *options)
    }
 }
 
-char *OutputWriter::get_options(char *dest)
+const char *OutputWriter::ow_quote_string(const char *str, POOLMEM *&b)
+{
+   if (flags & OF_USE_QUOTES) {
+      b = quote_string(b, str);
+      return b;
+   } else {
+      return str;
+   }
+}
+
+const char *OutputWriter::ow_quote_string(const char *str)
+{
+   return ow_quote_string(str, quote_buf);
+}
+
+const char *OutputWriter::ow_quote_string2(const char *str)
+{
+   return ow_quote_string(str, quote_buf2);
+}
+
+char *OutputWriter::get_options(char *dest, int len)
 {
    char ed1[50];
    *dest = *ed1 = 0;
    if (separator != OW_DEFAULT_SEPARATOR) {
       snprintf(dest, 50, "s%d", (int)separator);
    }
-   if (object_separator) {
-      snprintf(ed1, sizeof(ed1), "S%d", (int) object_separator);
-      bstrncat(dest, ed1, sizeof(ed1));
+   if (object_separator[0]) {
+      snprintf(ed1, sizeof(ed1), "S%d", (int) object_separator[0]);
+      bstrncat(dest, ed1, len);
    }
    if (timeformat != OW_DEFAULT_TIMEFORMAT) {
       snprintf(ed1, sizeof(ed1), "t%d", (int) timeformat);
-      bstrncat(dest, ed1, sizeof(ed1));
+      bstrncat(dest, ed1, len);
+   }
+   if (equal != OW_DEFAULT_EQUAL) {
+      snprintf(ed1, sizeof(ed1), "e%d", (int) equal);
+      bstrncat(dest, ed1, len);
+   }
+   if (strcmp(label, OW_DEFAULT_LABEL) != 0) {
+      snprintf(ed1, sizeof(ed1), "l%d", (int)label[0]);
+      bstrncat(dest, ed1, len);
    }
    if (flags & OF_USE_NEW_OUTPUT) {
-      bstrncat(dest, "o", 1);
+      bstrncat(dest, "o", len);
+   }
+   if (flags & OF_USE_QUOTES) {
+      bstrncat(dest, "q", len);
+   }
+   if (flags & OF_USE_JSON) {
+      bstrncat(dest, "j", len);
    }
    return dest;
 }
@@ -103,8 +179,10 @@ char *OutputWriter::get_options(char *dest)
 void OutputWriter::get_buf(bool append)
 {
    if (!buf) {
+      quote_buf = get_pool_memory(PM_MESSAGE);
+      quote_buf2 = get_pool_memory(PM_MESSAGE);
       buf = get_pool_memory(PM_MESSAGE);
-      *buf = 0;
+      *quote_buf2 = *quote_buf = *buf = 0;
 
    } else if (!append) {
       *buf = 0;
@@ -114,31 +192,63 @@ void OutputWriter::get_buf(bool append)
 char *OutputWriter::start_group(const char *name, bool append)
 {
    get_buf(append);
-   pm_strcat(buf, name);
-   pm_strcat(buf, ":\n");
-   return buf;
+   return get_output(OT_START_OBJ, OT_LABEL, name, OT_END);
 }
 
 char *OutputWriter::end_group(bool append)
 {
    get_buf(append);
-   pm_strcat(buf, "\n");
+   return get_output(OT_SEP,
+                     OT_INT32, "error", error,
+                     OT_STRING, "errmsg", NPRTB(errmsg),
+                     OT_END_OBJ,
+                     OT_NL,
+                     OT_END);
+}
 
-   return buf;
+char *OutputWriter::start_object(const char *name, bool append)
+{
+   get_buf(append);
+   return get_output(OT_LABEL, name, OT_START_OBJ, OT_END);
+}
+
+char *OutputWriter::end_object(bool append)
+{
+   get_buf(append);
+   return get_output(OT_END_OBJ, OT_END);
 }
 
 char *OutputWriter::start_list(const char *name, bool append)
 {
    get_buf(append);
-   pm_strcat(buf, name);
-   pm_strcat(buf, ": [\n");
+   if (use_json()) {
+      if (*buf) {
+         int l = strlen(buf);
+         if (buf[l - 1] != ',' && buf[l - 1] != ':' && buf[l - 1] != '{') {
+            pm_strcat(buf, ",");
+         }
+      }
+      pm_strcat(buf, ow_quote_string(name));
+      pm_strcat(buf, ":[");
+      need_separator = false;
+      will_need_separator = false;
+
+   } else {
+      pm_strcat(buf, name);
+      pm_strcat(buf, ": [\n");
+   }
    return buf;
 }
 
 char *OutputWriter::end_list(bool append)
 {
    get_buf(append);
-   pm_strcat(buf, "]\n");
+   if (use_json()) {
+      pm_strcat(buf, "]");
+
+   } else {
+      pm_strcat(buf, "]\n");
+   }
 
    return buf;
 }
@@ -193,6 +303,11 @@ char *OutputWriter::get_output(POOLMEM **out, OutputType first, ...)
    return ret;
 }
 
+bool OutputWriter::use_json()
+{
+   return flags & OF_USE_JSON;
+}
+
 char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
 {
    char       ed1[MAX_TIME_LENGTH];
@@ -205,10 +320,11 @@ char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
    char      *s = NULL, *k = NULL;
    alist     *lst;
    Plugin    *plug;
+   POOLMEM   *tmp3 = get_pool_memory(PM_FNAME);
    POOLMEM   *tmp2 = get_pool_memory(PM_FNAME);
    POOLMEM   *tmp = get_pool_memory(PM_FNAME);
    OutputType val = first;
-   *tmp2 = *tmp = 0;
+   *tmp3 = *tmp2 = *tmp = 0;
 
    while (val != OT_END) {
 
@@ -216,13 +332,33 @@ char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
 
       /* Some arguments are not using a keyword */
       switch (val) {
-      case OT_END:
+      case OT_SEP:
+         need_separator = true;
+         will_need_separator = false;
+         break;
+      case OT_NOP:
+         need_separator = false;
+         will_need_separator = false;
+         break;
+      case OT_CLEAR:
+         need_separator = false;
+         will_need_separator = false;
+         break;
       case OT_START_OBJ:
+      case OT_START_ARRAY:
+      case OT_START_HASH:
+         will_need_separator = false;
+         break;
+      case OT_NL:
+      case OT_END:
       case OT_END_OBJ:
-      case OT_CLEAR:
+      case OT_END_ARRAY:
+      case OT_END_HASH:
+         need_separator = false;
+         will_need_separator = true;
          break;
-
       default:
+         will_need_separator = true;
          k = va_arg(ap, char *);          /* Get the variable name */
 
          /* If requested, we can put the keyword in lowercase */
@@ -246,47 +382,51 @@ char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
       case OT_ALIST_STR:
          lst = va_arg(ap, alist *);
          i = 0;
-         Mmsg(tmp, "%s=", k);
+         Mmsg(tmp, "%s%c%s",
+              ow_quote_string(k), equal,
+              open_table);
          if (lst) {
             foreach_alist(s, lst) {
                if (i++ > 0) {
                   pm_strcat(tmp, ",");
                }
-               pm_strcat(tmp, s);
+               pm_strcat(tmp, ow_quote_string(s));
             }
          }
-         pm_strcat(tmp, separator_str);
+         pm_strcat(tmp, close_table);
          break;
       case OT_PLUGINS:
          lst = va_arg(ap, alist *);
          i = 0;
-         pm_strcpy(tmp, "plugins=");
+         Mmsg(tmp, "%s%c%s",
+              ow_quote_string("plugin"),
+              equal,
+              open_table);
          if (lst) {
             foreach_alist(plug, lst) {
                if (i++ > 0) {
                   pm_strcat(tmp, ",");
                }
-               pm_strcat(tmp, plug->name);
+               pm_strcat(tmp, ow_quote_string(plug->file));
             }
          }
-         pm_strcat(tmp, separator_str);
+         pm_strcat(tmp, close_table);
          break;
       case OT_RATIO:
          d = va_arg(ap, double);
-         Mmsg(tmp, "%s=%.2f%c", k, d, separator);
+         Mmsg(tmp, "%s%c%.2f", ow_quote_string(k), equal, d);
          break;
       case OT_BOOL:
          i = va_arg(ap, int);
-         Mmsg(tmp, "%s=%s%c", k, i?"true":"false", separator);
+         Mmsg(tmp, "%s%c%s", ow_quote_string(k), equal, i?"true":"false");
          break;
       case OT_STRING:
          s = va_arg(ap, char *);
-         Mmsg(tmp, "%s=%s%c", k, NPRTB(s), separator) ;
+         Mmsg(tmp, "%s%c%s", ow_quote_string(k), equal, ow_quote_string2(NPRTB(s))) ;
          break;
-
       case OT_INT32:
          i32 = va_arg(ap, int32_t);
-         Mmsg(tmp, "%s=%d%c", k, i32, separator);
+         Mmsg(tmp, "%s%c%d", ow_quote_string(k), equal, i32);
          break;
 
       case OT_UTIME:
@@ -310,39 +450,63 @@ char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
          default:
             bstrutime(ed1, sizeof(ed1), bt);
          }
-         Mmsg(tmp, "%s_epoch=%lld%c%s=%s%c", k, bt, separator, k, ed1, separator);
+         if (flags & OF_USE_QUOTES) {
+            ow_quote_string(ed1, tmp3);
+            bstrncpy(ed1, tmp3, sizeof(ed1));
+         }
+         Mmsg(tmp3, "%s_epoch", k);
+         Mmsg(tmp,
+              "%s%c%lld"
+              "%c"
+              "%s%c%s",
+              ow_quote_string(tmp3), equal, bt,
+              separator,
+              ow_quote_string2(k), equal, ed1);
          break;
 
       case OT_DURATION:
          bt = va_arg(ap, utime_t);     
-         bstrutime(ed1, sizeof(ed1), bt); 
-         Mmsg(tmp, "%s=%lld%c%s_str=%s%c", k, bt, separator, k, edit_utime(bt, ed1, sizeof(ed1)), separator);
+         bstrutime(ed1, sizeof(ed1), bt);
+         if (flags & OF_USE_QUOTES) {
+            ow_quote_string(edit_utime(bt, ed1, sizeof(ed1)), tmp3);
+            bstrncpy(ed1, tmp3, sizeof(ed1));
+         }
+
+         Mmsg(tmp3, "%s_str", k);
+         Mmsg(tmp, "%s%c%lld"
+              "%c"
+              "%s%c%s",
+              ow_quote_string(k), equal, bt,
+              separator,
+              ow_quote_string2(tmp3), equal, ed1);
          break;
 
       case OT_SIZE:
       case OT_INT64:
          i64 = va_arg(ap, int64_t);
-         Mmsg(tmp, "%s=%lld%c", k, i64, separator);
+         Mmsg(tmp, "%s%c%lld", ow_quote_string(k), equal, i64);
          break;
 
       case OT_PINT64:
          u64 = va_arg(ap, uint64_t);
-         Mmsg(tmp, "%s=%llu%c", k, u64, separator);
+         Mmsg(tmp, "%s%c%llu", ow_quote_string(k), equal, u64);
          break;
 
       case OT_INT:
          i64 = va_arg(ap, int);
-         Mmsg(tmp, "%s=%lld%c", k, i64, separator);
+         Mmsg(tmp, "%s%c%lld", ow_quote_string(k), equal, i64);
          break;
 
       case OT_JOBLEVEL:
       case OT_JOBTYPE:
       case OT_JOBSTATUS:
          i32 = va_arg(ap, int32_t);
-         if (i32 == 0) {
-            Mmsg(tmp, "%s=%c", k, separator);
+         if (i32 == 0) {        // TODO: Check if it's possible
+            Mmsg(tmp, "%s%c%s", ow_quote_string(k), equal, ow_quote_string2(""));
          } else {
-            Mmsg(tmp, "%s=%c%c", k, (char)i32, separator);
+            ed1[0] = (char)i32;
+            ed1[1] = 0;
+            Mmsg(tmp, "%s%c%s", ow_quote_string(k), equal, ow_quote_string2(ed1));
          }
          break;
 
@@ -351,20 +515,57 @@ char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
          break;
 
       case OT_END_OBJ:
-         pm_strcpy(tmp, "\n");
+         i=0;
+         if (object_separator[1]) {
+            tmp[i++] = object_separator[1];
+         } else {
+            tmp[i++] = '\n';
+         }
+         tmp[i] = 0;
+         break;
+
+      case OT_LABEL:
+         Mmsg(tmp, "%s%s", ow_quote_string(k), label);
+         will_need_separator = false;
          break;
 
       case OT_START_OBJ:
          i=0;
-         if (object_separator) {
-            for(; i < 32 ; i++) {
-               tmp[i] = object_separator;
-            }
+         if (object_separator[0]) {
+            tmp[i++] = object_separator[0];
+
+         } else {
+            tmp[i++] = '\n';
          }
-         tmp[i++] = '\n';
          tmp[i] = 0;
          break;
 
+      case OT_START_HASH:
+         pm_strcpy(tmp, open_hash);
+         break;
+
+      case OT_END_HASH:
+         pm_strcpy(tmp, close_hash);
+         break;
+
+      case OT_START_ARRAY:
+         pm_strcpy(tmp, open_table);
+         break;
+
+      case OT_END_ARRAY:
+         pm_strcpy(tmp, close_table);
+         break;
+
+      case OT_NL:
+         pm_strcat(tmp, "\n");
+         break;
+
+      case OT_NOP:
+         break;
+
+      case OT_SEP:
+         break;                 // Will add a separator at the next round
+
       case OT_END:
          /* wanted fallback */
       default:
@@ -372,6 +573,11 @@ char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
       }
 
       if (val != OT_END) {
+         if (need_separator) {
+            pm_strcat(out, separator_str);
+
+         }
+         need_separator = will_need_separator;
          pm_strcat(out, tmp);
          val = (OutputType) va_arg(ap, int); /* OutputType is promoted to int when using ... */
       }
@@ -379,6 +585,7 @@ char *OutputWriter::get_output(va_list ap, POOLMEM **out, OutputType first)
 
    free_pool_memory(tmp);
    free_pool_memory(tmp2);
+   free_pool_memory(tmp3);
    //Dmsg1(000, "%s", *out);
    return *out;
 }
@@ -405,8 +612,7 @@ int main(int argc, char **argv)
    int64_t     nb64 = -1;
    btime_t     t    = time(NULL);
 
-   ok(strcmp(wt.get_options(ed1), "") == 0, "Default options");
-
+   ok(strcmp(wt.get_options(ed1, sizeof(ed1)), "") == 0, "Default options");
    Pmsg1(000, "%s", wt.start_group("test"));
 
    wt.get_output(&tmp, OT_CLEAR,
@@ -437,7 +643,7 @@ int main(int argc, char **argv)
                  OT_END));
 
    wt.set_time_format(OTT_TIME_UNIX);
-   ok(strcmp("t1", wt.get_options(ed1)) == 0, "Check unix time format");
+   ok(strcmp("t1", wt.get_options(ed1, sizeof(ed1))) == 0, "Check unix time format");
 
    Pmsg1(000, "%s",
          wt.get_output(OT_CLEAR,
@@ -445,7 +651,7 @@ int main(int argc, char **argv)
                  OT_END));
 
    wt.set_time_format(OTT_TIME_NC);
-   ok(strcmp("t2", wt.get_options(ed1)) == 0, "Check NC time format");
+   ok(strcmp("t2", wt.get_options(ed1, sizeof(ed1))) == 0, "Check NC time format");
 
    Pmsg1(000, "%s",
          wt.get_output(OT_CLEAR,
@@ -455,18 +661,18 @@ int main(int argc, char **argv)
    Pmsg1(000, "%s", wt.end_group(false));
 
    wt.parse_options("s43t1O");
-   ok(strcmp(wt.get_options(ed1), "s43t1") == 0, "Check options after parsing");
+   ok(strcmp(wt.get_options(ed1, sizeof(ed1)), "s43t1") == 0, "Check options after parsing");
 
-   ok(strstr(
-         wt.get_output(OT_CLEAR,
-                 OT_BTIME,  "now",  t,
-                 OT_STRING, "brazil", "test",
-                 OT_END),
-         "+brazil=test+") != NULL,
+   char *p = wt.get_output(OT_CLEAR,
+                           OT_BTIME,  "now",  t,
+                           OT_STRING, "brazil", "test",
+                           OT_END);
+   ok(strstr(p, "+brazil=test") != NULL,
       "Check separator");
+   Pmsg1(000, "===> [%s]\n", p);
 
    wt.parse_options("CS35");
-   ok(strcmp(wt.get_options(ed1), "S35") == 0, "Check options after parsing");
+   ok(strcmp(wt.get_options(ed1, sizeof(ed1)), "S35") == 0, "Check options after parsing");
 
    Pmsg1(000, "%s",
          wt.get_output(OT_CLEAR,
@@ -480,8 +686,36 @@ int main(int argc, char **argv)
                  OT_END_OBJ,
                  OT_END));
 
-   free(str);
 
+   wt.parse_options("Cs44e58q");
+   ok(strcmp(wt.get_options(ed1, sizeof(ed1)), "s44e58q") == 0, "Check options after parsing");
+
+   wt.parse_options("Cj");
+   Dmsg1(0, "%s\n", wt.get_options(ed1, sizeof(ed1)));
+   ok(strcmp(wt.get_options(ed1, sizeof(ed1)), "s44S123e58l58oqj") == 0, "Check options after parsing");
+   
+   Pmsg1(000, "%s",
+         wt.get_output(OT_CLEAR,
+                       OT_START_ARRAY,
+                       OT_LABEL, "backup",
+                 OT_START_HASH,
+                 OT_STRING, "test", "my value",
+                 OT_STRING, "test2", ptr,
+                 OT_END_HASH,
+                       OT_LABEL, "restore",
+                 OT_START_HASH,
+                 OT_STRING, "test", "my value",
+                 OT_STRING, "test2", ptr,
+                 OT_END_HASH,
+                 OT_LABEL, "info",
+                 OT_START_HASH,
+                 OT_BTIME, "now", t,
+                 OT_STRING, "test2", ptr,
+                 OT_END_HASH,
+                 OT_END_ARRAY,
+                 OT_END));
+
+   free(str);
    return report();
 }
 #endif   /* TEST_PROGRAM */
index 01959db1d845278b92f5ae71785397445680ef8e..932248f275e824b0ac66837103e658d9e5a74c44 100644 (file)
@@ -43,9 +43,17 @@ enum _OutputType {
    OT_ALIST_STR,
    OT_BOOL,                     /* boolean */
 
+   OT_NL,                       /* Add new line */
+   OT_SEP,                      /* Add a separator */
+   OT_NOP,                      /* do nothing */
    OT_END,                      /* Last operator (no extra arg) */
    OT_START_OBJ,                /* Skip a line to start a new object (no extra arg) */
    OT_END_OBJ,                  /* Skip a line to end current object (no extra arg) */
+   OT_LABEL,                    /* label sep  ("test":) */
+   OT_START_ARRAY,              /* Start array [ */
+   OT_END_ARRAY,                /* End array ] */
+   OT_START_HASH,               /* Start hash { */
+   OT_END_HASH,                 /* End hash */
    OT_CLEAR,                    /* truncate current buffer (no extra arg) */
    OT_DURATION                  /* time duration in second */
 };
@@ -61,6 +69,15 @@ typedef enum {
 
 #define OW_DEFAULT_SEPARATOR  '\n'
 #define OW_DEFAULT_TIMEFORMAT OTT_TIME_ISO
+#define OW_DEFAULT_EQUAL '='
+
+#define OW_DEFAULT_OPEN_HASH ""
+#define OW_DEFAULT_CLOSE_HASH ""
+
+#define OW_DEFAULT_OPEN_TABLE ""
+#define OW_DEFAULT_CLOSE_TABLE ""
+
+#define OW_DEFAULT_LABEL ":\n"
 
 /* If included from output.c, mark the class as export (else, symboles are
  * exported from all files...
@@ -76,24 +93,58 @@ class OUTPUT_EXPORT OutputWriter: public SMARTALLOC
 private:
    void init() {
       buf = NULL;
+      quote_buf = NULL;
+      quote_buf2 = NULL;
       separator  = OW_DEFAULT_SEPARATOR;
       separator_str[0] = OW_DEFAULT_SEPARATOR;
       separator_str[1] = 0;
       timeformat = OW_DEFAULT_TIMEFORMAT;
-      object_separator = 0;
+      object_separator[0] = 0;
+      object_separator[1] = 0;
+      equal = OW_DEFAULT_EQUAL;
+      equal_str[0] = OW_DEFAULT_EQUAL;
+      equal_str[1] = 0;
+
+      open_hash = OW_DEFAULT_OPEN_HASH;
+      close_hash = OW_DEFAULT_CLOSE_HASH;
+      open_table = OW_DEFAULT_OPEN_TABLE;
+      close_table = OW_DEFAULT_CLOSE_TABLE;
+      label = OW_DEFAULT_LABEL;
       flags = 0;
+
+      need_separator=false;
+      will_need_separator=true;
+
+      error = 0;
+      errmsg = NULL; 
    };
 
 protected:
    virtual char   *get_output(va_list ap, POOLMEM **out, OutputType first);
    void            get_buf(bool append); /* Allocate buf if needed */
 
+   bool            quote_str;
    int             flags;
+   char            equal;
+   char            equal_str[2];
    char            separator;
    char            separator_str[2];
-   char            object_separator;
+   char            object_separator[2];
    OutputTimeType  timeformat;
    POOLMEM        *buf;
+   POOLMEM        *quote_buf;   /* Buffer used to quote parameters */
+   POOLMEM        *quote_buf2;
+   const char     *open_table;
+   const char     *close_table;
+   const char     *open_hash;
+   const char     *close_hash;
+   const char     *label;
+
+   int             error;
+   char           *errmsg;
+
+   bool       need_separator;
+   bool       will_need_separator;
 
 public:
    OutputWriter(const char *opts) {
@@ -107,6 +158,9 @@ public:
 
    virtual ~OutputWriter() {
       free_and_null_pool_memory(buf);
+      free_and_null_pool_memory(quote_buf);
+      free_and_null_pool_memory(quote_buf2);
+      bfree_and_null(errmsg);
    };
 
    /* s[ascii code]t[0-3]
@@ -116,7 +170,7 @@ public:
     * "s43t1" => + as separator and time as unix timestamp
     */
    virtual void  parse_options(const char *opts);
-   virtual char *get_options(char *dest_l128); /* MAX_NAME_LENGTH mini  */
+   virtual char *get_options(char *dest_l128, int len); /* MAX_NAME_LENGTH mini  */
 
    /* Make a clear separation in the output*/
    virtual char *start_group(const char *name, bool append=true);
@@ -126,19 +180,48 @@ public:
    virtual char *start_list(const char *name, bool append=true);
    virtual char *end_list(bool append=true);
 
+   void set_label(const char *l) {
+      label = l;
+   };
+
+   void set_error(int errcode, const char *errstr) {
+      error = errcode;
+      errmsg = bstrdup(errstr);
+   };
+   
+   void set_hash(const char *o, const char *c)
+   {
+      open_hash = o;
+      close_hash = c;
+   };
+
+   void set_table(const char *o, const char *c)
+   {
+      open_table = o;
+      close_table = c;
+   };
+   
    /* \n by default, can be \t for example */
    void set_separator(char sep) {
       separator = sep;
       separator_str[0] = sep;
    };
 
-   void set_object_separator(char sep) {
-      object_separator = sep;
+   void set_equal(char eq) {
+      equal = eq;
+      equal_str[0] = eq;
+   };
+   void set_object_separator(char sep, char esep) {
+      object_separator[0] = sep;
+      object_separator[1] = esep;
    };
-
    void set_time_format(OutputTimeType fmt) {
       timeformat = fmt;
    };
+   bool use_json();
+
+   char *start_object(const char *name, bool append=true);
+   char *end_object(bool append=true);
 
 /* Usage:
  *   get_output(&out,
@@ -152,12 +235,21 @@ public:
  *  "name=value\nage=10\nbirt-date=2012-01-12 10:20:00\nweight=100\n"
  *
  */
-
+   
    /* Use a user supplied buffer */
    char *get_output(POOLMEM **out, OutputType first, ...);
 
    /* Use the internal buffer */
    char *get_output(OutputType first, ...);
+
+   /* Quote or not the string, memory allocated in quote_buffer */
+   const char *ow_quote_string(const char *str);
+
+   /* Quote or not the string, memory allocated in quote_buffer2 */
+   const char *ow_quote_string2(const char *str);
+
+   /* Quote or not the string, memory allocated in quote_buffer2 */
+   const char *ow_quote_string(const char *str, POOLMEM *&buffer);
 };
 
 #endif
index 58e36e5ca5eed40a91bbb486f5fe6105db3e702c..27dcb07be85dad1b1077c138848390bf770cf30d 100644 (file)
@@ -62,25 +62,37 @@ static void list_terminated_jobs(STATUS_PKT *sp)
    OutputWriter ow(sp->api_opts);
    char dt[MAX_TIME_LENGTH], b1[30], b2[30];
    char level[10];
+   bool add_sep=false;
    struct s_last_job *je;
    const char *msg;
    char *p;
 
-   msg =  _("\nTerminated Jobs:\n");
-   if (!sp->api) sendit(msg, strlen(msg), sp);
+   if (sp->api > 1) {
+      ow.start_group("terminated");
+      p = ow.get_output(OT_START_ARRAY, OT_END);
+      sendit(p, strlen(p), sp);
+
+   } else if (!sp->api) {
+      msg =  _("\nTerminated Jobs:\n");
+      sendit(msg, strlen(msg), sp);
+   }
    if (last_jobs->size() == 0) {
-      if (!sp->api) sendit("====\n", 5, sp);
+      if (!sp->api) {
+         sendit("====\n", 5, sp);
+
+      } else if (sp->api > 1) {
+         ow.get_output(OT_CLEAR, OT_END_ARRAY, OT_END);
+         p = ow.end_group();
+         sendit(p, strlen(p), sp);
+      }
       return;
    }
+
    lock_last_jobs_list();
    msg =  _(" JobId  Level    Files      Bytes   Status   Finished        Name \n");
    if (!sp->api) sendit(msg, strlen(msg), sp);
    msg =  _("===================================================================\n");
    if (!sp->api) sendit(msg, strlen(msg), sp);
-   if (sp->api > 1) {
-      p = ow.start_group("terminated");
-      sendit(p, strlen(p), sp);
-   }
    foreach_dlist(je, last_jobs) {
       char JobName[MAX_NAME_LENGTH];
       const char *termstat;
@@ -134,17 +146,19 @@ static void list_terminated_jobs(STATUS_PKT *sp)
             *p = 0;
          }
       }
+      p = buf;
       if (sp->api == 1) {
          bsnprintf(buf, sizeof(buf), _("%6d\t%-6s\t%8s\t%10s\t%-7s\t%-8s\t%s\n"),
-            je->JobId,
-            level,
-            edit_uint64_with_commas(je->JobFiles, b1),
-            edit_uint64_with_suffix(je->JobBytes, b2),
-            termstat,
-            dt, JobName);
+                   je->JobId,
+                   level,
+                   edit_uint64_with_commas(je->JobFiles, b1),
+                   edit_uint64_with_suffix(je->JobBytes, b2),
+                   termstat,
+                   dt, JobName);
 
       } else if (sp->api > 1) {
          p = ow.get_output(OT_CLEAR,
+                           add_sep? OT_SEP : OT_NOP,
                            OT_START_OBJ,
                            OT_INT,     "jobid",     je->JobId,
                            OT_JOBLEVEL,"level",     je->JobLevel,
@@ -160,23 +174,25 @@ static void list_terminated_jobs(STATUS_PKT *sp)
                            OT_INT,     "errors",    je->Errors,
                            OT_END_OBJ,
                            OT_END);
-         sendit(p, strlen(p), sp);
+         add_sep = true;
+
       } else {
          bsnprintf(buf, sizeof(buf), _("%6d  %-6s %8s %10s  %-7s  %-8s %s\n"),
-            je->JobId,
-            level,
-            edit_uint64_with_commas(je->JobFiles, b1),
-            edit_uint64_with_suffix(je->JobBytes, b2),
-            termstat,
-            dt, JobName);
-         sendit(buf, strlen(buf), sp);
+                   je->JobId,
+                   level,
+                   edit_uint64_with_commas(je->JobFiles, b1),
+                   edit_uint64_with_suffix(je->JobBytes, b2),
+                   termstat,
+                   dt, JobName);
       }
+      sendit(p, strlen(p), sp);
    }
    unlock_last_jobs_list();
    if (!sp->api) {
       sendit("====\n", 5, sp);
    } else if (sp->api > 1) {
-      p = ow.end_group(false);
+      ow.get_output(OT_CLEAR, OT_END_ARRAY, OT_END);
+      p = ow.end_group();
       sendit(p, strlen(p), sp);
    }
 }
index 971ae055fb39258fee5800b24dc3812162d6e36e..d8b1f1c8c2d494a52f38f948b8935ab42fd9e4f3 100644 (file)
@@ -2562,9 +2562,8 @@ uint32_t cloud_dev::get_cloud_upload_transfer_status(POOL_MEM& msg, bool verbose
 
 void cloud_dev::get_api_cloud_upload_transfer_status(OutputWriter &ow, bool verbose)
 {
-   ow.start_group("uploads");
+   ow.get_output(OT_LABEL, "uploads", OT_END);
    upload_mgr.append_api_status(ow, verbose);
-   ow.end_group();
 }
 
 /* format a status message of the cloud transfers. Verbose gives details on each transfer */
@@ -2578,9 +2577,8 @@ uint32_t cloud_dev::get_cloud_download_transfer_status(POOL_MEM& msg, bool verbo
 
 void cloud_dev::get_api_cloud_download_transfer_status(OutputWriter &ow, bool verbose)
 {
-   ow.start_group("downloads");
+   ow.get_output(OT_LABEL, "downloads", OT_END);
    download_mgr.append_api_status(ow, verbose);
-   ow.end_group();
 }
 
 /* for a given volume VolumeName, return parts that is a list of the
index 0c319a08dc80edd142283fc1651821df3d805468..1d2477dc04ee645343413214984d019e88de47c4 100644 (file)
@@ -277,6 +277,7 @@ void transfer::append_api_status(OutputWriter &ow)
                   OT_DURATION, "duration",            m_stat_duration/ONE_SEC,
                   OT_STRING,"message",                NPRTB(m_message),
                   OT_INT32, "retry",                  m_retry,
+                  OT_END_OBJ,
                   OT_END);
    } else {
          ow.get_output(OT_START_OBJ,
@@ -290,6 +291,7 @@ void transfer::append_api_status(OutputWriter &ow)
                   OT_DURATION, "eta",                 m_stat_eta/ONE_SEC,
                   OT_STRING,"message",                NPRTB(m_message),
                   OT_INT32, "retry",                  m_retry,
+                  OT_END_OBJ,
                   OT_END);
    }
 }
@@ -817,4 +819,5 @@ void transfer_manager::append_api_status(OutputWriter &ow, bool verbose)
       }
       ow.end_list();
    }
+   ow.get_output(OT_END_OBJ, OT_END);
 }
index 4c8ac820e4083ef2771d090d44c8f58b4646ebbd..b56a08ff1660023a0421cd701d65b028a245e3e2 100644 (file)
@@ -43,7 +43,6 @@ static char DotStatusJob[] = "JobId=%d JobStatus=%c JobErrors=%d\n";
 /* Forward referenced functions */
 static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp);
 static void sendit(const char *msg, int len, void *arg);
-static void dbg_sendit(const char *msg, int len, void *arg);
 static void send_blocked_status(DEVICE *dev, STATUS_PKT *sp);
 static void send_device_status(DEVICE *dev, STATUS_PKT *sp);
 static void list_running_jobs(STATUS_PKT *sp);
@@ -193,9 +192,8 @@ void list_shstore(DEVICE *dev, OutputWriter *ow) {}
 bool list_shstore(DEVICE *dev, POOLMEM **msg, int *len) { return false;}
 #endif
 
-static void api_list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
+static void api_list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp, OutputWriter *ow)
 {
-   OutputWriter ow(sp->api_opts);
    int zero=0;
    int blocked=0;
    uint64_t f, t;
@@ -207,37 +205,37 @@ static void api_list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
 
    dev->get_freespace(&f, &t);
 
-   ow.get_output(OT_START_OBJ,
-                 OT_STRING, "name", dev->device->hdr.name,
-                 OT_STRING, "archive_device", dev->archive_name(),
-                 OT_STRING, "type", dev->print_type(),
-                 OT_STRING, "driver", dev->print_driver_type(),
-                 OT_STRING, "media_type", dev->device->media_type,
-                 OT_INT,    "open", (int)dev->is_open(),
-                 OT_INT,    "writers",      dev->num_writers,
-                 OT_INT32,  "maximum_concurrent_jobs", dev->max_concurrent_jobs,
-                 OT_INT64,  "maximum_volume_size", dev->max_volume_size,
-                 OT_INT,    "read_only", dev->device->read_only,
-                 OT_INT,    "autoselect", dev->device->autoselect,
-                 OT_INT,    "enabled", dev->enabled,
-                 OT_INT64,  "free_space", f,
-                 OT_INT64,  "total_space", t,
-                 OT_INT64,  "devno",     dev->devno,
-                 OT_END);
+   ow->get_output(OT_START_OBJ,
+                  OT_STRING, "name", dev->device->hdr.name,
+                  OT_STRING, "archive_device", dev->archive_name(),
+                  OT_STRING, "type", dev->print_type(),
+                  OT_STRING, "driver", dev->print_driver_type(),
+                  OT_STRING, "media_type", dev->device->media_type,
+                  OT_INT,    "open", (int)dev->is_open(),
+                  OT_INT,    "writers",      dev->num_writers,
+                  OT_INT32,  "maximum_concurrent_jobs", dev->max_concurrent_jobs,
+                  OT_INT64,  "maximum_volume_size", dev->max_volume_size,
+                  OT_INT,    "read_only", dev->device->read_only,
+                  OT_INT,    "autoselect", dev->device->autoselect,
+                  OT_INT,    "enabled", dev->enabled,
+                  OT_INT64,  "free_space", f,
+                  OT_INT64,  "total_space", t,
+                  OT_INT64,  "devno",     dev->devno,
+                  OT_END);
 
    if (dev->is_open()) {
       if (dev->is_labeled()) {
-         ow.get_output(OT_STRING, "mounted", dev->blocked()?"0":"1",
-                       OT_STRING, "waiting", dev->blocked()?"1":"0",
-                       OT_STRING, "volume",  dev->VolHdr.VolumeName,
-                       OT_STRING, "pool",    NPRTB(dev->pool_name),
-                       OT_END);
+         ow->get_output(OT_STRING, "mounted", dev->blocked()?"0":"1",
+                        OT_STRING, "waiting", dev->blocked()?"1":"0",
+                        OT_STRING, "volume",  dev->VolHdr.VolumeName,
+                        OT_STRING, "pool",    NPRTB(dev->pool_name),
+                        OT_END);
       } else {
-         ow.get_output(OT_INT,    "mounted", zero,
-                       OT_INT,    "waiting", zero,
-                       OT_STRING, "volume",  "",
-                       OT_STRING, "pool",    "",
-                       OT_END);
+         ow->get_output(OT_INT,    "mounted", zero,
+                        OT_INT,    "waiting", zero,
+                        OT_STRING, "volume",  "",
+                        OT_STRING, "pool",    "",
+                        OT_END);
       }
 
       blocked = 1;
@@ -265,29 +263,29 @@ static void api_list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
       /* TODO: give more information about blocked status
        * and the volume needed if WAITING for SYSOP
        */
-      ow.get_output(OT_STRING, "blocked_desc", NPRTB(p),
-                    OT_INT,    "blocked",      blocked,
-                    OT_END);
+      ow->get_output(OT_STRING, "blocked_desc", NPRTB(p),
+                     OT_INT,    "blocked",      blocked,
+                     OT_END);
 
-      ow.get_output(OT_INT, "append", (int)dev->can_append(),
-                    OT_END);
+      ow->get_output(OT_INT, "append", (int)dev->can_append(),
+                     OT_END);
 
       if (dev->can_append()) {
-         ow.get_output(OT_INT64, "bytes",  dev->VolCatInfo.VolCatBytes,
-                       OT_INT32, "blocks", dev->VolCatInfo.VolCatBlocks,
-                       OT_END);
+         ow->get_output(OT_INT64, "bytes",  dev->VolCatInfo.VolCatBytes,
+                        OT_INT32, "blocks", dev->VolCatInfo.VolCatBlocks,
+                        OT_END);
 
       } else {  /* reading */
-         ow.get_output(OT_INT64, "bytes",  dev->VolCatInfo.VolCatRBytes,
-                       OT_INT32, "blocks", dev->VolCatInfo.VolCatReads, /* might not be blocks */
-                       OT_END);
+         ow->get_output(OT_INT64, "bytes",  dev->VolCatInfo.VolCatRBytes,
+                        OT_INT32, "blocks", dev->VolCatInfo.VolCatReads, /* might not be blocks */
+                        OT_END);
 
       }
-      ow.get_output(OT_INT, "file",  dev->file,
-                    OT_INT, "block", dev->block_num,
-                    OT_END);
+      ow->get_output(OT_INT, "file",  dev->file,
+                     OT_INT, "block", dev->block_num,
+                     OT_END);
    } else {
-      ow.get_output(OT_INT,    "mounted", zero,
+      ow->get_output(OT_INT,    "mounted", zero,
                     OT_INT,    "waiting", zero,
                     OT_STRING, "volume",  "",
                     OT_STRING, "pool",    "",
@@ -301,14 +299,14 @@ static void api_list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
                     OT_END);
    }
 
-   list_shstore(dev, &ow);
+   list_shstore(dev, ow);
 
-   p = ow.get_output(OT_END_OBJ, OT_END);
+   p = ow->get_output(OT_END_OBJ, OT_END);
    sendit(p, strlen(p), sp);
 }
 
 
-static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
+static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp, OutputWriter *ow)
 {
    char b1[35], b2[35], b3[35];
    POOL_MEM msg(PM_MESSAGE);
@@ -316,7 +314,7 @@ static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
    int bpb;
 
    if (sp->api > 1) {
-      api_list_one_device(name, dev, sp);
+      api_list_one_device(name, dev, sp, ow);
       return;
    }
 
@@ -408,44 +406,34 @@ static void list_one_device(char *name, DEVICE *dev, STATUS_PKT *sp)
    if (!sp->api) sendit("==\n", 4, sp);
 }
 
-void _dbg_list_one_device(char *name, DEVICE *dev, const char *file, int line)
-{
-   STATUS_PKT sp;
-   sp.bs = NULL;
-   sp.callback = dbg_sendit;
-   sp.context = NULL;
-   d_msg(file, line, 0, "Called dbg_list_one_device():");
-   list_one_device(name, dev, &sp);
-   send_device_status(dev, &sp);
-}
-
-static void list_one_autochanger(char *name, AUTOCHANGER *changer, STATUS_PKT *sp)
+static void list_one_autochanger(char *name, AUTOCHANGER *changer, STATUS_PKT *sp, OutputWriter *ow)
 {
    int     len;
    char   *p;
    DEVRES *device;
    POOL_MEM msg(PM_MESSAGE);
-   OutputWriter ow(sp->api_opts);
 
    if (sp->api > 1) {
-      ow.get_output(OT_START_OBJ,
-                    OT_STRING,    "autochanger",  changer->hdr.name,
-                    OT_END);
+      ow->get_output(OT_START_OBJ,
+                     OT_STRING,    "autochanger",  changer->hdr.name,
+                     OT_END);
 
-      ow.start_group("devices");
+      ow->start_list("devices");
 
       foreach_alist(device, changer->device) {
-         ow.get_output(OT_START_OBJ,
-                       OT_STRING, "name",  device->hdr.name,
-                       OT_STRING, "device",device->device_name,
-                       OT_END_OBJ,
-                       OT_END);
+         // We build a list here with a CLEAR 
+         ow->get_output(OT_START_OBJ,
+                        OT_STRING, "name",  device->hdr.name,
+                        OT_STRING, "device",device->device_name,
+                        OT_END_OBJ,
+                        OT_END);
       }
 
-      ow.end_group();
+      ow->end_list();
 
-      p = ow.get_output(OT_END_OBJ, OT_END);
+      p = ow->get_output(OT_END_OBJ, OT_END);
       sendit(p, strlen(p), sp);
+      ow->get_output(OT_CLEAR, OT_END);
 
    } else {
 
@@ -470,24 +458,52 @@ static void list_devices(STATUS_PKT *sp, char *name)
    int len;
    DEVRES *device;
    AUTOCHANGER *changer;
-   POOL_MEM msg(PM_MESSAGE);
+   OutputWriter ow(sp->api_opts);
+   char *buf=NULL;
+   bool first;
 
    if (!sp->api) {
+      POOL_MEM msg(PM_MESSAGE);
       len = Mmsg(msg, _("\nDevice status:\n"));
       sendit(msg, len, sp);
+
+   } else if (sp->api > 1) {
+      ow.start_group("devices");
+      ow.get_output(OT_START_OBJ, OT_END);
+      buf = ow.start_list("autochanger");
+      sendit(buf, strlen(buf), sp);
    }
 
+   first = true;
    foreach_res(changer, R_AUTOCHANGER) {
       if (!name || strcmp(changer->hdr.name, name) == 0) {
-         list_one_autochanger(changer->hdr.name, changer, sp);
+         ow.get_output(OT_CLEAR, first? OT_NOP : OT_SEP, OT_END);
+         list_one_autochanger(changer->hdr.name, changer, sp, &ow);
+         first = false;
       }
    }
 
+   if (sp->api > 1) {
+      ow.get_output(OT_CLEAR, OT_END);
+      ow.end_list();
+      buf = ow.start_list("device");
+      sendit(buf, strlen(buf), sp);
+   }
+
+   first = true;
    foreach_res(device, R_DEVICE) {
       if (!name || strcmp(device->hdr.name, name) == 0) {
-         list_one_device(device->hdr.name, device->dev, sp);
+         ow.get_output(OT_CLEAR, first? OT_NOP : OT_SEP, OT_END);
+         list_one_device(device->hdr.name, device->dev, sp, &ow);
+         first = false;
       }
    }
+   if (sp->api > 1) {
+      ow.end_list();
+      ow.get_output(OT_END_OBJ, OT_END);
+      buf = ow.end_group();
+      sendit(buf, strlen(buf), sp);
+   }
    if (!sp->api) sendit("====\n\n", 6, sp);
 }
 
@@ -495,18 +511,25 @@ static void list_cloud_transfers(STATUS_PKT *sp, bool verbose)
 {
    if (sp->api) {
       DEVRES *device;
+
       foreach_res(device, R_DEVICE) {
          if (device->dev && device->dev->is_cloud()) {
-            cloud_dev *cdev = (cloud_dev*)device->dev;
+            char *p;
             OutputWriter ow(sp->api_opts);
-            ow.start_group(device->hdr.name);
+            cloud_dev *cdev = (cloud_dev*)device->dev;
+
+            ow.start_group("cloud");
+            ow.get_output(OT_START_OBJ, OT_END);
             cdev->get_api_cloud_upload_transfer_status(ow, verbose);
             cdev->get_api_cloud_download_transfer_status(ow, verbose);
-            ow.end_group();
-            char *p = ow.get_output(OT_END_OBJ, OT_END);
+            ow.get_output(OT_END_OBJ, OT_END);
+            p = ow.end_group();
             sendit(p, strlen(p), sp);
+            break;              // One transfer manager?
          }
       }
+
+
    } else {
       bool first=true;
       int len;
@@ -545,6 +568,7 @@ static void api_list_sd_status_header(STATUS_PKT *sp)
    sd_list_loaded_drivers(&drivers);
    wt.start_group("header");
    wt.get_output(
+      OT_START_OBJ,
       OT_STRING, "name",        my_name,
       OT_STRING, "version",     VERSION " (" BDATE ")",
       OT_STRING, "uname",       HOST_OS " " DISTNAME " " DISTVER,
@@ -561,6 +585,7 @@ static void api_list_sd_status_header(STATUS_PKT *sp)
       OT_INT64,  "debug",       debug_level,
       OT_INT,    "trace",       get_trace(),
       OT_ALIST_STR, "tags",     debug_get_tags_list(&tlist, debug_level_tags),
+      OT_END_OBJ,
       OT_END);
    p = wt.end_group();
    sendit(p, strlen(p), sp);
@@ -799,23 +824,31 @@ static void api_list_running_jobs(STATUS_PKT *sp)
    JCR *jcr;
    DCR *dcr, *rdcr;
    time_t now = time(NULL);
+   bool add_sep=false;
+   char *p;
+
+   ow.start_group("running");
+   p = ow.get_output(OT_START_ARRAY, OT_END);
+   sendit(p, strlen(p), sp);
 
    foreach_jcr(jcr) {
+      p = ow.get_output(OT_CLEAR, add_sep ? OT_SEP : OT_NOP, OT_START_OBJ, OT_END);
+      add_sep = true;
+
       if (jcr->JobId == 0 && jcr->dir_bsock) {
          int val = (jcr->dir_bsock && jcr->dir_bsock->tls)?1:0;
-         p1 = ow.get_output(OT_CLEAR,
-                            OT_UTIME, "DirectorConnected", (utime_t)jcr->start_time,
+         p1 = ow.get_output(OT_UTIME, "DirectorConnected", (utime_t)jcr->start_time,
                             OT_INT, "DirTLS", val,
+                            OT_END_OBJ,
                             OT_END);
          sendit(p1, strlen(p1), sp);
          continue;
       }
       if (jcr->getJobType() == JT_SYSTEM) {
+         add_sep = false;       // Skip this one, it has been printed already
          continue;
       }
-      ow.get_output(OT_CLEAR,
-                    OT_START_OBJ,
-                    OT_INT32,   "jobid",     jcr->JobId,
+      ow.get_output(OT_INT32,   "jobid",     jcr->JobId,
                     OT_STRING,  "job",       jcr->Job,
                     OT_JOBLEVEL,"level",     jcr->getJobLevel(),
                     OT_JOBTYPE, "type",      jcr->getJobType(),
@@ -901,6 +934,10 @@ static void api_list_running_jobs(STATUS_PKT *sp)
    }
    endeach_jcr(jcr);
 
+   // Finish the JSON output if needed
+   ow.get_output(OT_CLEAR, OT_END_ARRAY, OT_END);
+   p = ow.end_group();
+   sendit(p, strlen(p), sp);
 }
 
 static void list_running_jobs(STATUS_PKT *sp)
@@ -1073,13 +1110,6 @@ static void sendit(POOL_MEM &msg, int len, STATUS_PKT *sp)
    }
 }
 
-static void dbg_sendit(const char *msg, int len, void *sp)
-{
-   if (len > 0) {
-      Dmsg0(-1, msg);
-   }
-}
-
 /*
  * Status command from Director
  */
@@ -1293,7 +1323,9 @@ static void api_collectors_status(STATUS_PKT *sp, char *collname)
    POOLMEM *buf;
 
    Dmsg1(200, "enter api_collectors_status() %s\n", NPRTB(collname));
-   ow.start_group("collector_backends");
+   ow.start_group("collector");
+   ow.get_output(OT_START_OBJ, OT_END);
+   ow.start_list("collector_backends");
    LockRes();
    foreach_res(res, R_COLLECTOR) {
       if (collname && !bstrcmp(collname, res->res_collector.hdr.name)){
@@ -1303,12 +1335,14 @@ static void api_collectors_status(STATUS_PKT *sp, char *collname)
       api_render_collector_status(res->res_collector, ow);
    };
    UnlockRes();
-   buf = ow.end_group();
-   if (!collname){
-      ow.start_group("collector_update");
+   ow.end_list();
+   if (!collname) {
+      ow.get_output(OT_SEP, OT_LABEL, "collector_update", OT_END);
       api_render_updcollector_status(ow);
-      buf = ow.end_group();
+
    }
+   ow.get_output(OT_END_OBJ, OT_END);
+   buf = ow.end_group();
    sendit(buf, strlen(buf), sp);
    Dmsg0(200, "leave api_collectors_status()\n");
 };