From: Eric Bollengier Date: Mon, 4 Oct 2021 16:57:37 +0000 (+0200) Subject: Add api v2 json output X-Git-Tag: Beta-15.0.0~864 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2f487060b9bac358bda37dc8604084392a5f3a47;p=thirdparty%2Fbacula.git Add api v2 json output bconsole .api 2 api_opts=j .status dir header --- diff --git a/bacula/src/dird/ua_purge.c b/bacula/src/dird/ua_purge.c index f23c14040..44bd518e2 100644 --- a/bacula/src/dird/ua_purge.c +++ b/bacula/src/dird/ua_purge.c @@ -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 diff --git a/bacula/src/dird/ua_status.c b/bacula/src/dird/ua_status.c index 40a75867c..a8b2d865c 100644 --- a/bacula/src/dird/ua_status.c +++ b/bacula/src/dird/ua_status.c @@ -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"); }; diff --git a/bacula/src/filed/status.c b/bacula/src/filed/status.c index 38b32b6dd..7b30cef8b 100644 --- a/bacula/src/filed/status.c +++ b/bacula/src/filed/status.c @@ -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"); }; diff --git a/bacula/src/lib/lib.h b/bacula/src/lib/lib.h index 14425c38f..c7df548f2 100644 --- a/bacula/src/lib/lib.h +++ b/bacula/src/lib/lib.h @@ -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" diff --git a/bacula/src/lib/output.c b/bacula/src/lib/output.c index 10a75f371..d703a44d0 100644 --- a/bacula/src/lib/output.c +++ b/bacula/src/lib/output.c @@ -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 */ diff --git a/bacula/src/lib/output.h b/bacula/src/lib/output.h index 01959db1d..932248f27 100644 --- a/bacula/src/lib/output.h +++ b/bacula/src/lib/output.h @@ -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 diff --git a/bacula/src/lib/status.h b/bacula/src/lib/status.h index 58e36e5ca..27dcb07be 100644 --- a/bacula/src/lib/status.h +++ b/bacula/src/lib/status.h @@ -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); } } diff --git a/bacula/src/stored/cloud_dev.c b/bacula/src/stored/cloud_dev.c index 971ae055f..d8b1f1c8c 100644 --- a/bacula/src/stored/cloud_dev.c +++ b/bacula/src/stored/cloud_dev.c @@ -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 diff --git a/bacula/src/stored/cloud_transfer_mgr.c b/bacula/src/stored/cloud_transfer_mgr.c index 0c319a08d..1d2477dc0 100644 --- a/bacula/src/stored/cloud_transfer_mgr.c +++ b/bacula/src/stored/cloud_transfer_mgr.c @@ -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); } diff --git a/bacula/src/stored/status.c b/bacula/src/stored/status.c index 4c8ac820e..b56a08ff1 100644 --- a/bacula/src/stored/status.c +++ b/bacula/src/stored/status.c @@ -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"); };