From 4b0a7cf92f72e408d415695e77e80ee75b599f98 Mon Sep 17 00:00:00 2001 From: Joerg Steffens Date: Fri, 6 Mar 2015 18:15:14 +0100 Subject: [PATCH] improved JSON support In order to better interact with other programs, the Bareos Director console now supports the ".api json" command, to switch to JSON output. Currently only a subset of the commands are supported (the ones required by bareos-webui). To archieve this, Bareos can be linked against the jansson library. Other parts of Bareos (output of the configuration options) now also use the jansson library. --- autoconf/configure.in | 41 +++ platforms/packaging/bareos-Univention_4.0.dsc | 2 +- platforms/packaging/bareos.dsc | 2 +- platforms/packaging/bareos.spec | 2 + src/cats/cats.h | 11 +- src/cats/protos.h | 22 +- src/cats/sql.c | 145 +++++--- src/cats/sql_list.c | 76 ++-- src/console/Makefile.in | 5 + src/console/console.c | 15 +- src/console/console_conf.c | 32 +- src/dird/Makefile.in | 6 + src/dird/dird.c | 5 +- src/dird/dird_conf.c | 199 +++++------ src/dird/inc_conf.c | 12 +- src/dird/protos.h | 8 + src/dird/sd_cmds.c | 2 +- src/dird/ua.h | 3 +- src/dird/ua_cmds.c | 25 +- src/dird/ua_dotcmds.c | 338 ++++++++++++------ src/dird/ua_output.c | 50 +-- src/dird/ua_query.c | 10 +- src/dird/ua_restore.c | 12 +- src/dird/ua_run.c | 2 +- src/dird/ua_select.c | 4 +- src/dird/ua_server.c | 5 + src/dird/ua_update.c | 17 +- src/filed/Makefile.in | 23 +- src/filed/filed.c | 5 +- src/filed/filed_conf.c | 32 +- src/include/baconfig.h | 6 + src/lib/Makefile.in | 42 ++- src/lib/json.c | 59 +++ src/lib/lib.h | 1 + src/lib/mem_pool.c | 62 +++- src/lib/mem_pool.h | 6 + src/lib/output_formatter.c | 291 +++++++++++++++ src/lib/output_formatter.h | 88 +++++ src/lib/parse_conf.h | 19 +- src/lib/protos.h | 3 + src/lib/res.c | 117 ++---- src/qt-tray-monitor/tray-monitor.cpp | 5 +- src/qt-tray-monitor/tray-monitor.pro.in | 1 + src/qt-tray-monitor/tray_conf.cpp | 32 +- src/stored/Makefile.in | 10 + src/stored/stored.c | 5 +- src/stored/stored_conf.c | 36 +- src/win32/lib/Makefile | 5 +- 48 files changed, 1326 insertions(+), 573 deletions(-) create mode 100644 src/lib/json.c create mode 100644 src/lib/output_formatter.c create mode 100644 src/lib/output_formatter.h diff --git a/autoconf/configure.in b/autoconf/configure.in index 83409313b78..e273204bb6d 100644 --- a/autoconf/configure.in +++ b/autoconf/configure.in @@ -3077,6 +3077,46 @@ AC_SUBST(FASTLZ_INC) AC_SUBST(FASTLZ_LIBS) AC_SUBST(FASTLZ_LIBS_NONSHARED) + +dnl +dnl Check for jansson (json library) +dnl +JANSSON_LIBS="-ljansson" +JANSSON_INC="" +have_jansson=no +AC_ARG_WITH(jansson, + AC_HELP_STRING([--with-jansson@<:@=DIR@:>@], [Directory holding jansson includes/libs]), + with_jansson_directory=${withval} +) + +if test "x${with_jansson_directory}" != "xyes" && test x"${with_jansson_directory}" != "x"; then + # + # Make sure the $with_jansson_directory also makes sense + # + if test -d "${with_jansson_directory}/lib" -a -d "${with_jansson_directory}/include"; then + have_jansson=yes + JANSSON_INC="-I${with_jansson_directory}/include ${JANSSON_INC}" + JANSSON_LIBS="-L${with_jansson_directory}/lib ${JANSSON_LIBS}" + fi +else + if test x$with_jansson_directory = xyes || test x"${with_jansson_directory}" = "x" && $PKGCONFIG jansson; then + have_jansson=yes + JANSSON_INC=`$PKGCONFIG --cflags janson` + JANSSON_LIBS=`$PKGCONFIG --libs jansson` + fi +fi + +if test "x${have_jansson}" = "xyes"; then + AC_DEFINE(HAVE_JANSSON, 1, [Define to 1 if you have jansson lib]) +else + JANSSON_LIBS="" + JANSSON_INC="" +fi + +AC_SUBST(JANSSON_INC) +AC_SUBST(JANSSON_LIBS) + + dnl dnl Check if we have AFS on this system dnl @@ -4705,6 +4745,7 @@ Configuration on `date`: ZLIB support: ${have_zlib} LZO support: ${have_lzo} FASTLZ support: ${have_fastlz} + JANSSON support: ${have_jansson} LMDB support: ${support_lmdb} NDMP support: ${support_ndmp} enable-smartalloc: ${support_smartalloc} diff --git a/platforms/packaging/bareos-Univention_4.0.dsc b/platforms/packaging/bareos-Univention_4.0.dsc index 067832d0402..c302634122c 100644 --- a/platforms/packaging/bareos-Univention_4.0.dsc +++ b/platforms/packaging/bareos-Univention_4.0.dsc @@ -6,7 +6,7 @@ Version: 15.2.0 Maintainer: Joerg Steffens Homepage: http://www.bareos.org/ Standards-Version: 3.9.4 -Build-Depends: acl-dev, autotools-dev, bc, chrpath, debhelper (>= 7.0.50~), dpkg-dev (>= 1.13.19), git-core, libacl1-dev, libcap-dev, libfastlz-dev, liblzo2-dev, libqt4-dev, libreadline-dev, libssl-dev, libwrap0-dev, libx11-dev, libsqlite3-dev, libmysqlclient-dev, libpq-dev (>= 8.4), lsb-release, mtx, ncurses-dev, openssl, pkg-config, po-debconf (>= 0.8.2), python-dev, ucslint, univention-config-dev, zlib1g-dev +Build-Depends: acl-dev, autotools-dev, bc, chrpath, debhelper (>= 7.0.50~), dpkg-dev (>= 1.13.19), git-core, libacl1-dev, libcap-dev, libfastlz-dev, libjansson-dev, liblzo2-dev, libqt4-dev, libreadline-dev, libssl-dev, libwrap0-dev, libx11-dev, libsqlite3-dev, libmysqlclient-dev, libpq-dev (>= 8.4), lsb-release, mtx, ncurses-dev, openssl, pkg-config, po-debconf (>= 0.8.2), python-dev, ucslint, univention-config-dev, zlib1g-dev Build-Conflicts: python2.2-dev, python2.3, python2.4, qt3-dev-tools DEBTRANSFORM-RELEASE: 1 Files: diff --git a/platforms/packaging/bareos.dsc b/platforms/packaging/bareos.dsc index 516b4ac7145..f9759553d4e 100644 --- a/platforms/packaging/bareos.dsc +++ b/platforms/packaging/bareos.dsc @@ -6,7 +6,7 @@ Version: 15.2.0 Maintainer: Joerg Steffens Homepage: http://www.bareos.org/ Standards-Version: 3.9.4 -Build-Depends: acl-dev, autotools-dev, bc, chrpath, debhelper (>= 7.0.50~), dpkg-dev (>= 1.13.19), git-core, libacl1-dev, libcap-dev, libfastlz-dev, liblzo2-dev, libqt4-dev, libreadline-dev, libssl-dev, libwrap0-dev, libx11-dev, libsqlite3-dev, libmysqlclient-dev, libpq-dev, lsb-release, mtx, ncurses-dev, openssl, pkg-config, po-debconf (>= 0.8.2), python-dev, zlib1g-dev, libcmocka-dev +Build-Depends: acl-dev, autotools-dev, bc, chrpath, debhelper (>= 7.0.50~), dpkg-dev (>= 1.13.19), git-core, libacl1-dev, libcap-dev, libcmocka-dev, libfastlz-dev, libjansson-dev, liblzo2-dev, libqt4-dev, libreadline-dev, libssl-dev, libwrap0-dev, libx11-dev, libsqlite3-dev, libmysqlclient-dev, libpq-dev, lsb-release, mtx, ncurses-dev, openssl, pkg-config, po-debconf (>= 0.8.2), python-dev, zlib1g-dev, libcmocka-dev Build-Conflicts: python2.2-dev, python2.3, python2.4, qt3-dev-tools DEBTRANSFORM-RELEASE: 1 Files: diff --git a/platforms/packaging/bareos.spec b/platforms/packaging/bareos.spec index 75b0ecc0fb3..11dc1f16f87 100644 --- a/platforms/packaging/bareos.spec +++ b/platforms/packaging/bareos.spec @@ -196,6 +196,7 @@ BuildRequires: update-desktop-files %if 0%{?suse_version} > 1010 # link identical files BuildRequires: fdupes +BuildRequires: libjansson-devel BuildRequires: lsb-release %endif @@ -225,6 +226,7 @@ BuildRequires: fedora-release %endif %if 0%{?rhel_version} >= 600 || 0%{?centos_version} >= 600 || 0%{?fedora_version} >= 14 +BuildRequires: jansson-devel BuildRequires: tcp_wrappers-devel %endif diff --git a/src/cats/cats.h b/src/cats/cats.h index df2d28cfe8b..8ab5b51ae90 100644 --- a/src/cats/cats.h +++ b/src/cats/cats.h @@ -637,9 +637,8 @@ class LIST_CTX { int32_t num_rows; e_list_type type; /* Vertical/Horizontal */ - DB_LIST_HANDLER *send; /* send data back */ + OUTPUT_FORMATTER *send; /* send data back */ bool once; /* Used to print header one time */ - void *ctx; /* send() user argument */ B_DB *mdb; JCR *jcr; @@ -650,17 +649,16 @@ class LIST_CTX { void send_dashes() { if (*line) { - send(ctx, line); + send->decoration(line); } } - LIST_CTX(JCR *j, B_DB *m, DB_LIST_HANDLER *h, void *c, e_list_type t) { + LIST_CTX(JCR *j, B_DB *m, OUTPUT_FORMATTER *h, e_list_type t) { line[0] = '\0'; once = false; num_rows = 0; type = t; send = h; - ctx = c; jcr = j; mdb = m; } @@ -670,8 +668,7 @@ class LIST_CTX { * Some functions exported by sql.c for use within the cats directory. */ int list_result(void *vctx, int cols, char **row); -int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type); -void list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx); +int list_result(JCR *jcr, B_DB *mdb, OUTPUT_FORMATTER *send, e_list_type type); int get_sql_record_max(JCR *jcr, B_DB *mdb); bool check_tables_version(JCR *jcr, B_DB *mdb); bool db_check_max_connections(JCR *jcr, B_DB *mdb, uint32_t nb); diff --git a/src/cats/protos.h b/src/cats/protos.h index 793ba37cd97..c38f0b8bb21 100644 --- a/src/cats/protos.h +++ b/src/cats/protos.h @@ -118,17 +118,17 @@ int db_get_ndmp_level_mapping(JCR *jcr, B_DB *mdb, JOB_DBR *jr, char *filesystem bool db_get_ndmp_environment_string(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_RESULT_HANDLER *result_handler, void *ctx); /* sql_list.c */ -void db_list_pool_records(JCR *jcr, B_DB *db, POOL_DBR *pr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type); -void db_list_job_records(JCR *jcr, B_DB *db, JOB_DBR *jr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type); -void db_list_job_totals(JCR *jcr, B_DB *db, JOB_DBR *jr, DB_LIST_HANDLER sendit, void *ctx); -void db_list_files_for_job(JCR *jcr, B_DB *db, uint32_t jobid, DB_LIST_HANDLER sendit, void *ctx); -void db_list_media_records(JCR *jcr, B_DB *mdb, MEDIA_DBR *mdbr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); -void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, JobId_t JobId, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); -void db_list_joblog_records(JCR *jcr, B_DB *mdb, JobId_t JobId, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); -bool db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER *sendit, void *ctx, bool verbose, e_list_type type); -void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); -void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *jobids, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type); -void db_list_base_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx); +void db_list_pool_records(JCR *jcr, B_DB *db, POOL_DBR *pr, OUTPUT_FORMATTER *sendit, e_list_type type); +void db_list_job_records(JCR *jcr, B_DB *db, JOB_DBR *jr, OUTPUT_FORMATTER *sendit, e_list_type type); +void db_list_job_totals(JCR *jcr, B_DB *db, JOB_DBR *jr, OUTPUT_FORMATTER *sendit); +void db_list_files_for_job(JCR *jcr, B_DB *db, uint32_t jobid, OUTPUT_FORMATTER *sendit); +void db_list_media_records(JCR *jcr, B_DB *mdb, MEDIA_DBR *mdbr, OUTPUT_FORMATTER *sendit, e_list_type type); +void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, JobId_t JobId, OUTPUT_FORMATTER *sendit, e_list_type type); +void db_list_joblog_records(JCR *jcr, B_DB *mdb, JobId_t JobId, OUTPUT_FORMATTER *sendit, e_list_type type); +bool db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, OUTPUT_FORMATTER *sendit, bool verbose, e_list_type type); +void db_list_client_records(JCR *jcr, B_DB *mdb, OUTPUT_FORMATTER *sendit, e_list_type type); +void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *jobids, OUTPUT_FORMATTER *sendit, e_list_type type); +void db_list_base_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, OUTPUT_FORMATTER *sendit); /* sql_pooling.c */ bool db_sql_pool_initialize(const char *db_drivername, diff --git a/src/cats/sql.c b/src/cats/sql.c index 771fbb30ad0..491bc45859a 100644 --- a/src/cats/sql.c +++ b/src/cats/sql.c @@ -426,7 +426,7 @@ static int max_length(int max_length) /* * List dashes as part of header for listing SQL results in a table */ -void list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx) +static void list_dashes(B_DB *mdb, OUTPUT_FORMATTER *send) { SQL_FIELD *field; int i, j; @@ -434,7 +434,7 @@ void list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx) int num_fields; sql_field_seek(mdb, 0); - send(ctx, "+"); + send->decoration("+"); num_fields = sql_num_fields(mdb); for (i = 0; i < num_fields; i++) { field = sql_fetch_field(mdb); @@ -443,34 +443,39 @@ void list_dashes(B_DB *mdb, DB_LIST_HANDLER *send, void *ctx) } len = max_length(field->max_length + 2); for (j = 0; j < len; j++) { - send(ctx, "-"); + send->decoration("-"); } - send(ctx, "+"); + send->decoration("+"); } - send(ctx, "\n"); + send->decoration("\n"); } /* Small handler to print the last line of a list xxx command */ +/* static void last_line_handler(void *vctx, const char *str) { LIST_CTX *ctx = (LIST_CTX *)vctx; bstrncat(ctx->line, str, sizeof(ctx->line)); } +*/ int list_result(void *vctx, int nb_col, char **row) { SQL_FIELD *field; int i, col_len, max_len = 0; int num_fields; - char buf[2000], ewc[30]; + char ewc[30]; + POOL_MEM key; + POOL_MEM value; LIST_CTX *pctx = (LIST_CTX *)vctx; - DB_LIST_HANDLER *send = pctx->send; + OUTPUT_FORMATTER *send = pctx->send; e_list_type type = pctx->type; B_DB *mdb = pctx->mdb; - void *ctx = pctx->ctx; JCR *jcr = pctx->jcr; + send->object_start("row"); + num_fields = sql_num_fields(mdb); switch (type) { case NF_LIST: @@ -527,10 +532,9 @@ int list_result(void *vctx, int nb_col, char **row) /* * Keep the result to display the same line at the end of the table */ - list_dashes(mdb, last_line_handler, pctx); - send(ctx, pctx->line); + list_dashes(mdb, send); - send(ctx, "|"); + send->decoration("|"); sql_field_seek(mdb, 0); for (i = 0; i < num_fields; i++) { Dmsg1(800, "list_result looking at field %d\n", i); @@ -539,11 +543,10 @@ int list_result(void *vctx, int nb_col, char **row) break; } max_len = max_length(field->max_length); - bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name); - send(ctx, buf); + send->decoration(" %-*s |", max_len, field->name); } - send(ctx, "\n"); - list_dashes(mdb, send, ctx); + send->decoration("\n"); + list_dashes(mdb, send); } break; default: @@ -561,37 +564,41 @@ int list_result(void *vctx, int nb_col, char **row) break; } if (row[i] == NULL) { - bsnprintf(buf, sizeof(buf), " %s", "NULL"); + value.bsprintf(" %s", "NULL"); } else { - bsnprintf(buf, sizeof(buf), " %s", row[i]); + value.bsprintf(" %s", row[i]); } - send(ctx, buf); + send->object_key_value(field->name, value.c_str(), "%s"); } if (type != RAW_LIST) { - send(ctx, "\n"); + send->decoration("\n"); } break; case HORZ_LIST: Dmsg1(800, "list_result starts third loop looking at %d fields\n", num_fields); sql_field_seek(mdb, 0); - send(ctx, "|"); + send->decoration("|"); for (i = 0; i < num_fields; i++) { field = sql_fetch_field(mdb); if (!field) { break; } + max_len = max_length(field->max_length); if (row[i] == NULL) { - bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL"); + value.bsprintf(" %-*s |", max_len, "NULL"); } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) { - bsnprintf(buf, sizeof(buf), " %*s |", max_len, - add_commas(row[i], ewc)); + value.bsprintf(" %*s |", max_len, add_commas(row[i], ewc)); } else { - bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]); + value.bsprintf(" %-*s |", max_len, row[i]); } - send(ctx, buf); + + /* + * Use value format string to send preformated value. + */ + send->object_key_value(field->name, row[i], value.c_str()); } - send(ctx, "\n"); + send->decoration("\n"); break; case VERT_LIST: Dmsg1(800, "list_result starts vertical list at %d fields\n", num_fields); @@ -602,20 +609,27 @@ int list_result(void *vctx, int nb_col, char **row) break; } if (row[i] == NULL) { - bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL"); + key.bsprintf(" %*s: ", max_len, field->name); + value.bsprintf("%s\n", "NULL"); } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) { - bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, - add_commas(row[i], ewc)); + key.bsprintf(" %*s: ", max_len, field->name); + value.bsprintf("%s\n", add_commas(row[i], ewc)); } else { - bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]); + key.bsprintf(" %*s: ", max_len, field->name); + value.bsprintf("%s\n", row[i]); } - send(ctx, buf); + + /* + * Use value format string to send preformated value. + */ + send->object_key_value(field->name, key.c_str(), row[i], value.c_str()); } - send(ctx, "\n"); + send->decoration("\n"); break; default: break; } + send->object_end("row"); return 0; } @@ -624,17 +638,20 @@ int list_result(void *vctx, int nb_col, char **row) * list on one line horizontally. * Return number of rows */ -int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type type) +int list_result(JCR *jcr, B_DB *mdb, OUTPUT_FORMATTER *send, e_list_type type) { SQL_FIELD *field; SQL_ROW row; int i, col_len, max_len = 0; int num_fields; - char buf[2000], ewc[30]; + char ewc[30]; + POOL_MEM key; + POOL_MEM value; Dmsg0(800, "list_result starts\n"); if (sql_num_rows(mdb) == 0) { - send(ctx, _("No results to list.\n")); + send->decoration(_("No results to list.\n")); + send->object_end("table"); return sql_num_rows(mdb); } @@ -688,6 +705,7 @@ int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_ty case RAW_LIST: Dmsg1(800, "list_result starts second loop looking at %d fields\n", num_fields); while ((row = sql_fetch_row(mdb)) != NULL) { + send->object_start(row[0]); sql_field_seek(mdb, 0); for (i = 0; i < num_fields; i++) { field = sql_fetch_field(mdb); @@ -695,21 +713,22 @@ int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_ty break; } if (row[i] == NULL) { - bsnprintf(buf, sizeof(buf), " %s", "NULL"); + value.bsprintf(" %s", "NULL"); } else { - bsnprintf(buf, sizeof(buf), " %s", row[i]); + value.bsprintf(" %s", row[i]); } - send(ctx, buf); + send->object_key_value(field->name, value.c_str(), "%s"); } if (type != RAW_LIST) { - send(ctx, "\n"); + send->decoration("\n"); } + send->object_end(row[0]); } break; case HORZ_LIST: Dmsg1(800, "list_result starts second loop looking at %d fields\n", num_fields); - list_dashes(mdb, send, ctx); - send(ctx, "|"); + list_dashes(mdb, send); + send->decoration("|"); sql_field_seek(mdb, 0); for (i = 0; i < num_fields; i++) { Dmsg1(800, "list_result looking at field %d\n", i); @@ -718,16 +737,16 @@ int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_ty break; } max_len = max_length(field->max_length); - bsnprintf(buf, sizeof(buf), " %-*s |", max_len, field->name); - send(ctx, buf); + send->decoration(" %-*s |", max_len, field->name); } - send(ctx, "\n"); - list_dashes(mdb, send, ctx); + send->decoration("\n"); + list_dashes(mdb, send); Dmsg1(800, "list_result starts third loop looking at %d fields\n", num_fields); while ((row = sql_fetch_row(mdb)) != NULL) { + send->object_start(row[0]); sql_field_seek(mdb, 0); - send(ctx, "|"); + send->decoration("|"); for (i = 0; i < num_fields; i++) { field = sql_fetch_field(mdb); if (!field) { @@ -735,22 +754,26 @@ int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_ty } max_len = max_length(field->max_length); if (row[i] == NULL) { - bsnprintf(buf, sizeof(buf), " %-*s |", max_len, "NULL"); + value.bsprintf(" %-*s |", max_len, "NULL"); } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) { - bsnprintf(buf, sizeof(buf), " %*s |", max_len, - add_commas(row[i], ewc)); + value.bsprintf(" %*s |", max_len, add_commas(row[i], ewc)); } else { - bsnprintf(buf, sizeof(buf), " %-*s |", max_len, row[i]); + value.bsprintf(" %-*s |", max_len, row[i]); + } + if (i == num_fields-1) { + value.strcat("\n"); } - send(ctx, buf); + /* use value format string to send preformated value */ + send->object_key_value(field->name, row[i], value.c_str()); } - send(ctx, "\n"); + send->object_end(row[0]); } - list_dashes(mdb, send, ctx); + list_dashes(mdb, send); break; case VERT_LIST: Dmsg1(800, "list_result starts vertical list at %d fields\n", num_fields); while ((row = sql_fetch_row(mdb)) != NULL) { + send->object_start(row[0]); sql_field_seek(mdb, 0); for (i = 0; i < num_fields; i++) { field = sql_fetch_field(mdb); @@ -758,16 +781,20 @@ int list_result(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_ty break; } if (row[i] == NULL) { - bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, "NULL"); + key.bsprintf(" %*s: ", max_len, field->name); + value.bsprintf("%s\n", "NULL"); } else if (sql_field_is_numeric(mdb, field->type) && !jcr->gui && is_an_integer(row[i])) { - bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, - add_commas(row[i], ewc)); + key.bsprintf(" %*s: ", max_len, field->name); + value.bsprintf("%s\n", add_commas(row[i], ewc)); } else { - bsnprintf(buf, sizeof(buf), " %*s: %s\n", max_len, field->name, row[i]); + key.bsprintf(" %*s: ", max_len, field->name); + value.bsprintf("%s\n", row[i]); } - send(ctx, buf); + /* use value format string to send preformated value */ + send->object_key_value(field->name, key.c_str(), row[i], value.c_str()); } - send(ctx, "\n"); + send->decoration("\n"); + send->object_end(row[0]); } break; } diff --git a/src/cats/sql_list.c b/src/cats/sql_list.c index 2eea4668719..a3bc1626458 100644 --- a/src/cats/sql_list.c +++ b/src/cats/sql_list.c @@ -44,8 +44,8 @@ /* * Submit general SQL query */ -bool db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER *sendit, - void *ctx, bool verbose, e_list_type type) +bool db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, OUTPUT_FORMATTER *sendit, + bool verbose, e_list_type type) { bool retval = false; @@ -53,12 +53,14 @@ bool db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER * if (!sql_query(mdb, query, QF_STORE_RESULT)) { Mmsg(mdb->errmsg, _("Query failed: %s\n"), sql_strerror(mdb)); if (verbose) { - sendit(ctx, mdb->errmsg); + sendit->decoration(mdb->errmsg); } goto bail_out; } - list_result(jcr, mdb, sendit, ctx, type); + sendit->object_start("query"); + list_result(jcr, mdb, sendit, type); + sendit->object_end("query"); sql_free_result(mdb); retval = true; @@ -68,7 +70,7 @@ bool db_list_sql_query(JCR *jcr, B_DB *mdb, const char *query, DB_LIST_HANDLER * } void db_list_pool_records(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr, - DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) + OUTPUT_FORMATTER *sendit, e_list_type type) { char esc[MAX_ESCAPE_NAME_LENGTH]; @@ -103,7 +105,9 @@ void db_list_pool_records(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr, goto bail_out; } - list_result(jcr, mdb, sendit, ctx, type); + sendit->object_start("pools"); + list_result(jcr, mdb, sendit, type); + sendit->object_end("pools"); sql_free_result(mdb); @@ -111,7 +115,7 @@ void db_list_pool_records(JCR *jcr, B_DB *mdb, POOL_DBR *pdbr, db_unlock(mdb); } -void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) +void db_list_client_records(JCR *jcr, B_DB *mdb, OUTPUT_FORMATTER *sendit, e_list_type type) { db_lock(mdb); if (type == VERT_LIST) { @@ -127,7 +131,9 @@ void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void * goto bail_out; } - list_result(jcr, mdb, sendit, ctx, type); + sendit->object_start("clients"); + list_result(jcr, mdb, sendit, type); + sendit->object_end("clients"); sql_free_result(mdb); @@ -140,7 +146,7 @@ void db_list_client_records(JCR *jcr, B_DB *mdb, DB_LIST_HANDLER *sendit, void * * otherwise, list the Volumes in the Pool specified by PoolId */ void db_list_media_records(JCR *jcr, B_DB *mdb, MEDIA_DBR *mdbr, - DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) + OUTPUT_FORMATTER *sendit, e_list_type type) { char ed1[50]; char esc[MAX_ESCAPE_NAME_LENGTH]; @@ -188,7 +194,9 @@ void db_list_media_records(JCR *jcr, B_DB *mdb, MEDIA_DBR *mdbr, goto bail_out; } - list_result(jcr, mdb, sendit, ctx, type); + sendit->object_start("media"); + list_result(jcr, mdb, sendit, type); + sendit->object_end("media"); sql_free_result(mdb); @@ -197,7 +205,7 @@ void db_list_media_records(JCR *jcr, B_DB *mdb, MEDIA_DBR *mdbr, } void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, uint32_t JobId, - DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) + OUTPUT_FORMATTER *sendit, e_list_type type) { char ed1[50]; @@ -230,7 +238,9 @@ void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, uint32_t JobId, goto bail_out; } - list_result(jcr, mdb, sendit, ctx, type); + sendit->object_start("jobmedia"); + list_result(jcr, mdb, sendit, type); + sendit->object_end("jobmedia"); sql_free_result(mdb); @@ -239,7 +249,7 @@ void db_list_jobmedia_records(JCR *jcr, B_DB *mdb, uint32_t JobId, } void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds, - DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) + OUTPUT_FORMATTER *send, e_list_type type) { POOL_MEM str_limit(PM_MESSAGE); POOL_MEM str_jobids(PM_MESSAGE); @@ -269,12 +279,14 @@ void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds, if (sql_num_rows(mdb)) { if (JobIds && JobIds[0]) { - sendit(ctx, _("These JobIds have copies as follows:\n")); + send->decoration(_("These JobIds have copies as follows:\n")); } else { - sendit(ctx, _("The catalog contains copies as follows:\n")); + send->decoration(_("The catalog contains copies as follows:\n")); } - list_result(jcr, mdb, sendit, ctx, type); + send->object_start("copies"); + list_result(jcr, mdb, send, type); + send->object_end("copies"); } sql_free_result(mdb); @@ -284,7 +296,7 @@ void db_list_copies_records(JCR *jcr, B_DB *mdb, uint32_t limit, char *JobIds, } void db_list_joblog_records(JCR *jcr, B_DB *mdb, uint32_t JobId, - DB_LIST_HANDLER *sendit, void *ctx, e_list_type type) + OUTPUT_FORMATTER *sendit, e_list_type type) { char ed1[50]; @@ -310,7 +322,9 @@ void db_list_joblog_records(JCR *jcr, B_DB *mdb, uint32_t JobId, goto bail_out; } - list_result(jcr, mdb, sendit, ctx, type); + sendit->object_start("joblog"); + list_result(jcr, mdb, sendit, type); + sendit->object_end("joblog"); sql_free_result(mdb); @@ -324,8 +338,8 @@ void db_list_joblog_records(JCR *jcr, B_DB *mdb, uint32_t JobId, * Currently, we return all jobs or if jr->JobId is set, * only the job with the specified id. */ -void db_list_job_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit, - void *ctx, e_list_type type) +void db_list_job_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, OUTPUT_FORMATTER *sendit, + e_list_type type) { char ed1[50]; char limit[100]; @@ -386,7 +400,9 @@ void db_list_job_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *send if (!QUERY_DB(jcr, mdb, mdb->cmd)) { goto bail_out; } - list_result(jcr, mdb, sendit, ctx, type); + sendit->object_start("jobs"); + list_result(jcr, mdb, sendit, type); + sendit->object_end("jobs"); sql_free_result(mdb); @@ -398,7 +414,7 @@ void db_list_job_records(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *send * List Job totals * */ -void db_list_job_totals(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendit, void *ctx) +void db_list_job_totals(JCR *jcr, B_DB *mdb, JOB_DBR *jr, OUTPUT_FORMATTER *sendit) { db_lock(mdb); @@ -410,7 +426,7 @@ void db_list_job_totals(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendi goto bail_out; } - list_result(jcr, mdb, sendit, ctx, HORZ_LIST); + list_result(jcr, mdb, sendit, HORZ_LIST); sql_free_result(mdb); @@ -422,7 +438,7 @@ void db_list_job_totals(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendi goto bail_out; } - list_result(jcr, mdb, sendit, ctx, HORZ_LIST); + list_result(jcr, mdb, sendit, HORZ_LIST); sql_free_result(mdb); @@ -430,10 +446,10 @@ void db_list_job_totals(JCR *jcr, B_DB *mdb, JOB_DBR *jr, DB_LIST_HANDLER *sendi db_unlock(mdb); } -void db_list_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx) +void db_list_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, OUTPUT_FORMATTER *sendit) { char ed1[50]; - LIST_CTX lctx(jcr, mdb, sendit, ctx, NF_LIST); + LIST_CTX lctx(jcr, mdb, sendit, NF_LIST); db_lock(mdb); @@ -466,9 +482,11 @@ void db_list_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER * edit_int64(jobid, ed1), ed1); } + sendit->object_start(); if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) { goto bail_out; } + sendit->object_end(); sql_free_result(mdb); @@ -476,10 +494,10 @@ void db_list_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER * db_unlock(mdb); } -void db_list_base_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HANDLER *sendit, void *ctx) +void db_list_base_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, OUTPUT_FORMATTER *sendit) { char ed1[50]; - LIST_CTX lctx(jcr, mdb, sendit, ctx, NF_LIST); + LIST_CTX lctx(jcr, mdb, sendit, NF_LIST); db_lock(mdb); @@ -504,9 +522,11 @@ void db_list_base_files_for_job(JCR *jcr, B_DB *mdb, JobId_t jobid, DB_LIST_HAND edit_int64(jobid, ed1)); } + sendit->object_start("files"); if (!db_big_sql_query(mdb, mdb->cmd, list_result, &lctx)) { goto bail_out; } + sendit->object_end("files"); sql_free_result(mdb); diff --git a/src/console/Makefile.in b/src/console/Makefile.in index 6a8ff8a1175..23e88b68c3f 100644 --- a/src/console/Makefile.in +++ b/src/console/Makefile.in @@ -28,6 +28,8 @@ CONS_INC = @CONS_INC@ CONS_LIBS = @CONS_LIBS@ CONS_LDFLAGS = @CONS_LDFLAGS@ +JANSSON_CPPFLAGS = @JANSSON_INC@ + INCLUDES += -I$(srcdir) -I$(basedir) -I$(basedir)/include .SUFFIXES: .c .o @@ -43,6 +45,9 @@ all: Makefile bconsole @STATIC_CONS@ @echo "==== Make of console is good ====" @echo " " +console_conf.o: console_conf.c + @echo "Compiling $<" + $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(CPPFLAGS) $(CONS_INC) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< bconsole: Makefile $(CONSOBJS) ../lib/libbareos$(DEFAULT_ARCHIVE_TYPE) ../lib/libbareoscfg$(DEFAULT_ARCHIVE_TYPE) $(LIBTOOL_LINK) $(CXX) $(LDFLAGS) $(CONS_LDFLAGS) -L../lib -L../cats -o $@ $(CONSOBJS) \ diff --git a/src/console/console.c b/src/console/console.c index 8cdfc4554d8..d4820132eb3 100644 --- a/src/console/console.c +++ b/src/console/console.c @@ -299,8 +299,10 @@ static void read_and_process_input(FILE *input, BSOCK *UA_sock) tid = start_bsock_timer(UA_sock, timeout); while ((status = UA_sock->recv()) >= 0 || - (status == BNET_SIGNAL && (UA_sock->msglen == BNET_START_RTREE || - UA_sock->msglen == BNET_END_RTREE))) { + ((status == BNET_SIGNAL) && ( + (UA_sock->msglen != BNET_EOD) && + (UA_sock->msglen != BNET_MAIN_PROMPT) && + (UA_sock->msglen != BNET_SUB_PROMPT)))) { if (status == BNET_SIGNAL) { if (UA_sock->msglen == BNET_START_RTREE) { file_selection = true; @@ -321,7 +323,9 @@ static void read_and_process_input(FILE *input, BSOCK *UA_sock) * Suppress output if running in background or user hit ctl-c */ if (!stop && !usrbrk()) { - sendit(UA_sock->msg); + if (UA_sock->msg) { + sendit(UA_sock->msg); + } } } stop_bsock_timer(tid); @@ -1201,11 +1205,12 @@ int main(int argc, char *argv[]) } if (export_config_schema) { + POOL_MEM buffer; + my_config = new_config_parser(); init_cons_config(my_config, configfile, M_ERROR_TERM); - POOL_MEM buffer; print_config_schema_json(buffer); - printf( "%s\n", buffer.c_str() ); + printf("%s\n", buffer.c_str()); exit(0); } diff --git a/src/console/console_conf.c b/src/console/console_conf.c index 6d9de72a1ea..161cd380ff5 100644 --- a/src/console/console_conf.c +++ b/src/console/console_conf.c @@ -41,6 +41,7 @@ * Kern Sibbald, January MM, September MM */ +#define NEED_JANSSON_NAMESPACE 1 #include "bareos.h" #include "console_conf.h" @@ -357,31 +358,40 @@ bool parse_cons_config(CONFIG *config, const char *configfile, int exit_code) /* * Print configuration file schema in json format */ +#ifdef HAVE_JANSSON bool print_config_schema_json(POOL_MEM &buffer) { RES_TABLE *resources = my_config->m_resources; - add_json_object_start(buffer, 0, ""); + initialize_json(); - add_json_pair(buffer, 1, "format-version", 2); - add_json_pair(buffer, 1, "component", "bconsole"); - add_json_pair(buffer, 1, "version", VERSION); + json_t *json = json_object(); + json_object_set_new(json, "format-version", json_integer(2)); + json_object_set_new(json, "component", json_string("bconsole")); + json_object_set_new(json, "version", json_string(VERSION)); /* * Resources */ - add_json_object_start(buffer, 1, "resource"); - add_json_object_start(buffer, 2, "bconsole"); + json_t *resource = json_object(); + json_object_set(json, "resource", resource); + json_t *bconsole = json_object(); + json_object_set(resource, "bconsole", bconsole); for (int r = 0; resources[r].name; r++) { RES_TABLE resource = my_config->m_resources[r]; - print_items_schema_json(buffer, 3, resource.name, resource.items, !resources[r + 1].name); + json_object_set(bconsole, resource.name, json_items(resource.items)); } - add_json_object_end(buffer, 2, "bconsole", true); - add_json_object_end(buffer, 1, "resource", true); - - add_json_object_end(buffer, 0, "", true); + pm_strcat(buffer, json_dumps(json, JSON_INDENT(2))); + json_decref(json); return true; } +#else +bool print_config_schema_json(POOL_MEM &buffer) +{ + pm_strcat(buffer, "{ \"success\": false, \"message\": \"not available\" }"); + return false; +} +#endif diff --git a/src/dird/Makefile.in b/src/dird/Makefile.in index 226c59f97fd..17f25cc5ea2 100644 --- a/src/dird/Makefile.in +++ b/src/dird/Makefile.in @@ -47,6 +47,8 @@ TSTFNDOBJS = $(TSTFNDSRCS:.c=.o) INCLUDES += -I$(srcdir) -I$(basedir) -I$(basedir)/include +JANSSON_CPPFLAGS = @JANSSON_INC@ + .SUFFIXES: .c .o .PHONY: .DONTCARE: @@ -60,6 +62,10 @@ all: Makefile bareos-dir bareos-dbcheck @STATIC_DIR@ @echo "==== Make of dird is good ====" @echo " " +dird_conf.o: dird_conf.c + @echo "Compiling $<" + $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + bareos-dir: Makefile $(SVROBJS) \ ../lib/libbareos$(DEFAULT_ARCHIVE_TYPE) \ ../lib/libbareoscfg$(DEFAULT_ARCHIVE_TYPE) \ diff --git a/src/dird/dird.c b/src/dird/dird.c index 86f482f4b66..72cf30e6794 100644 --- a/src/dird/dird.c +++ b/src/dird/dird.c @@ -279,11 +279,12 @@ int main (int argc, char *argv[]) } if (export_config_schema) { + POOL_MEM buffer; + my_config = new_config_parser(); init_dir_config(my_config, configfile, M_ERROR_TERM); - POOL_MEM buffer; print_config_schema_json(buffer); - printf( "%s\n", buffer.c_str() ); + printf("%s\n", buffer.c_str()); goto bail_out; } diff --git a/src/dird/dird_conf.c b/src/dird/dird_conf.c index 96e37421e2c..8cb64cf8f3c 100644 --- a/src/dird/dird_conf.c +++ b/src/dird/dird_conf.c @@ -43,6 +43,7 @@ * Kern Sibbald, January MM */ +#define NEED_JANSSON_NAMESPACE 1 #include "bareos.h" #include "dird.h" @@ -735,126 +736,100 @@ struct s_kw VolumeStatus[] = { { NULL, 0 } }; -static bool print_item_schema_json(POOL_MEM &buff, int level, s_jl *item, const bool last) +#ifdef HAVE_JANSSON +json_t *json_item(s_jl *item) { - add_json_object_start(buff, level, item->level_name); - add_json_pair(buff, level + 1, "level", item->level, false); - add_json_pair(buff, level + 1, "type", item->job_type, true); - add_json_object_end(buff, level, item->level_name, last); + json_t *json = json_object(); - return true; + json_object_set_new(json, "level", json_integer(item->level)); + json_object_set_new(json, "type", json_integer(item->job_type)); + + return json; } -static bool print_item_schema_json(POOL_MEM &buff, int level, s_jt *item, const bool last) +json_t *json_item(s_jt *item) { - add_json_object_start(buff, level, item->type_name); - add_json_pair(buff, level + 1, "type", item->job_type, true); - add_json_object_end(buff, level, item->type_name, last); + json_t *json = json_object(); - return true; + json_object_set_new(json, "type", json_integer(item->job_type)); + + return json; } -static void add_json_datatype_start(POOL_MEM &buffer, int level, const int type, const char *typeclass, bool values) +json_t *json_datatype_header(const int type, const char *typeclass) { - bool last = false; - const char *datatype = datatype_to_str(type); + json_t *json = json_object(); const char *description = datatype_to_description(type); - add_json_object_start(buffer, level, datatype); - - if (!(description || typeclass || values)) { - last = true; - } - - add_json_pair(buffer, level + 1, "number", type, last); + json_object_set_new(json, "number", json_integer(type)); if (description) { - if (!(typeclass || values)) { - last = true; - } - add_json_pair(buffer, level + 1, "description", description, last); + json_object_set_new(json, "description", json_string(description)); } if (typeclass) { - if (!values) { - last = true; - } - add_json_pair(buffer, level + 1, "class", typeclass, last); + json_object_set_new(json, "class", json_string(typeclass)); } - if (values) { - add_json_object_start(buffer, level + 1, "values"); - } + return json; } -static void add_json_datatype_end(POOL_MEM &buffer, int level, const int type, const bool values, const bool last = false) +json_t *json_datatype(const int type) { - const char *datatype = datatype_to_str(type); - - if (values) { - add_json_object_end(buffer, level + 1, "values", true); - } - add_json_object_end(buffer, level, datatype, last); + return json_datatype_header(type, NULL); } -static bool print_datatype_schema_json(POOL_MEM &buffer, int level, const int type, const bool last = false) +json_t *json_datatype(const int type, s_kw items[]) { - add_json_datatype_start(buffer, level, type, NULL, false); - add_json_datatype_end(buffer, level, type, false, last); - - return true; -} - -static bool print_datatype_schema_json(POOL_MEM &buffer, int level, const int type, s_kw items[], const bool last = false) -{ - add_json_datatype_start(buffer, level, type, "keyword", items); + json_t *json = json_datatype_header(type, "keyword"); if (items) { + json_t *values = json_object(); for (int i = 0; items[i].name; i++) { - print_item_schema_json(buffer, level + 2, &items[i], !items[i + 1].name); + json_object_set_new(values, items[i].name, json_item(&items[i])); } + json_object_set_new(json, "values", values); } - add_json_datatype_end(buffer, level, type, items, last); - - return true; + return json; } -static bool print_datatype_schema_json(POOL_MEM &buffer, int level, const int type, s_jl items[], const bool last = false) +json_t *json_datatype(const int type, s_jl items[]) { - add_json_datatype_start(buffer, level, type, "keyword", items); + // FIXME: level_name keyword is not unique + json_t *json = json_datatype_header(type, "keyword"); if (items) { + json_t *values = json_object(); for (int i = 0; items[i].level_name; i++) { - print_item_schema_json(buffer, level + 2, &items[i], !items[i + 1].level_name); + json_object_set_new(values, items[i].level_name, json_item(&items[i])); } + json_object_set_new(json, "values", values); } - add_json_datatype_end(buffer, level, type, items, last); - - return true; + return json; } -static bool print_datatype_schema_json(POOL_MEM &buffer, int level, const int type, s_jt items[], const bool last = false) +json_t *json_datatype(const int type, s_jt items[]) { - add_json_datatype_start(buffer, level, type, "keyword", items); + json_t *json = json_datatype_header(type, "keyword"); if (items) { + json_t *values = json_object(); for (int i = 0; items[i].type_name; i++) { - print_item_schema_json(buffer, level + 2, &items[i], !items[i + 1].type_name); + json_object_set_new(values, items[i].type_name, json_item(&items[i])); } + json_object_set_new(json, "values", values); } - add_json_datatype_end(buffer, level, type, items, last); - - return true; + return json; } -bool print_datatype_schema_json(POOL_MEM &buffer, int level, const int type, RES_ITEM items[], const bool last) +json_t *json_datatype(const int type, RES_ITEM items[]) { - add_json_datatype_start(buffer, level, type, "sub", items); + json_t *json = json_datatype_header(type, "sub"); if (items) { + json_t *values = json_object(); + json_object_set_new(json, "values", values); for (int i = 0; items[i].name; i++) { - print_item_schema_json(buffer, level + 2, &items[i], !items[i + 1].name); + json_object_set_new(values, items[i].name, json_item(&items[i])); } } - add_json_datatype_end(buffer, level, type, items, last); - - return true; + return json; } /* @@ -862,101 +837,103 @@ bool print_datatype_schema_json(POOL_MEM &buffer, int level, const int type, RES */ bool print_config_schema_json(POOL_MEM &buffer) { - bool last = true; - bool datatype_last; DATATYPE_NAME *datatype; RES_TABLE *resources = my_config->m_resources; - add_json_object_start(buffer, 0, ""); + initialize_json(); - add_json_pair(buffer, 1, "format-version", 2); - add_json_pair(buffer, 1, "component", "bareos-dir"); - add_json_pair(buffer, 1, "version", VERSION); + json_t *json = json_object(); + json_object_set_new(json, "format-version", json_integer(2)); + json_object_set_new(json, "component", json_string("bareos-dir")); + json_object_set_new(json, "version", json_string(VERSION)); /* * Resources */ - add_json_object_start(buffer, 1, "resource"); - add_json_object_start(buffer, 2, "bareos-dir"); + json_t *resource = json_object(); + json_object_set(json, "resource", resource); + json_t *bareos_dir = json_object(); + json_object_set(resource, "bareos-dir", bareos_dir); for (int r = 0; resources[r].name; r++) { RES_TABLE resource = my_config->m_resources[r]; - print_items_schema_json(buffer, 3, resource.name, resource.items, !resources[r + 1].name); + json_object_set(bareos_dir, resource.name, json_items(resource.items)); } - add_json_object_end(buffer, 2, "bareos-dir", last); - add_json_object_end(buffer, 1, "resource"); - /* * Datatypes */ - add_json_object_start(buffer, 1, "datatype"); + json_t *json_datatype_obj = json_object(); + json_object_set(resource, "datatype", json_datatype_obj); int d = 0; while (get_datatype(d)->name != NULL) { datatype = get_datatype(d); - datatype_last = !(get_datatype(d + 1)->name); switch (datatype->number) { case CFG_TYPE_RUNSCRIPT: - print_datatype_schema_json(buffer, 2, CFG_TYPE_RUNSCRIPT, runscript_items, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_RUNSCRIPT, runscript_items)); break; case CFG_TYPE_INCEXC: - print_incexc_schema_json(buffer, 2, CFG_TYPE_INCEXC, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_incexc(CFG_TYPE_INCEXC)); break; case CFG_TYPE_OPTIONS: - print_options_schema_json(buffer, 2, CFG_TYPE_OPTIONS, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_options(CFG_TYPE_OPTIONS)); break; case CFG_TYPE_PROTOCOLTYPE: - print_datatype_schema_json(buffer, 2, CFG_TYPE_PROTOCOLTYPE, backupprotocols, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_PROTOCOLTYPE, backupprotocols)); break; case CFG_TYPE_AUTHPROTOCOLTYPE: - print_datatype_schema_json(buffer, 2, CFG_TYPE_AUTHPROTOCOLTYPE, authprotocols, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_AUTHPROTOCOLTYPE, authprotocols)); break; case CFG_TYPE_AUTHTYPE: - print_datatype_schema_json(buffer, 2, CFG_TYPE_AUTHTYPE, authmethods, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_AUTHTYPE, authmethods)); break; case CFG_TYPE_LEVEL: - print_datatype_schema_json(buffer, 2, CFG_TYPE_LEVEL, joblevels, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_LEVEL, joblevels)); break; case CFG_TYPE_JOBTYPE: - print_datatype_schema_json(buffer, 2, CFG_TYPE_JOBTYPE, jobtypes, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_JOBTYPE, jobtypes)); break; case CFG_TYPE_MIGTYPE: - print_datatype_schema_json(buffer, 2, CFG_TYPE_MIGTYPE, migtypes, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_MIGTYPE, migtypes)); break; case CFG_TYPE_REPLACE: - print_datatype_schema_json(buffer, 2, CFG_TYPE_REPLACE, ReplaceOptions, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_REPLACE, ReplaceOptions)); break; case CFG_TYPE_ACTIONONPURGE: - print_datatype_schema_json(buffer, 2, CFG_TYPE_ACTIONONPURGE, ActionOnPurgeOptions, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_ACTIONONPURGE, ActionOnPurgeOptions)); break; case CFG_TYPE_RUN: - print_datatype_schema_json(buffer, 2, CFG_TYPE_RUN, RunFields, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_RUN, RunFields)); break; default: - print_datatype_schema_json(buffer, 2, datatype->number, datatype_last); + json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(datatype->number)); break; } d++; } /* - * Only used in ua_dotcmds, not a datatype + * following datatypes are ignored: + * - VolumeStatus: only used in ua_dotcmds, not a datatype + * - FS_option_kw: from inc_conf. Replaced by CFG_TYPE_OPTIONS", options_items. + * - FS_options: are they needed? */ - //print_datatype_schema_json(buffer, 2, "VolumeStatus", VolumeStatus); - - // from inc_conf - // replaced by "CFG_TYPE_OPTIONS", options_items - //print_items_schema_json(buffer, 2, "FS_option_kw", FS_option_kw); - //TODO? s_fs_opt FS_options - add_json_object_end(buffer, 1, "datatype", last); - - add_json_object_end(buffer, 0, "", last); + pm_strcat(buffer, json_dumps(json, JSON_INDENT(2))); + json_decref(json); return true; } +#else +bool print_config_schema_json(POOL_MEM &buffer) +{ + pm_strcat(buffer, "{ \"success\": false, \"message\": \"not available\" }"); + return false; +} +#endif + /* * Propagate the settings from source BRSRES to dest BRSRES using the RES_ITEMS array. @@ -1215,7 +1192,7 @@ static void indent_config_item(POOL_MEM &cfg_str, int level, const char *config_ static inline void print_config_runscript(RES_ITEM *item, POOL_MEM &cfg_str) { POOL_MEM temp; - RUNSCRIPT* runscript; + RUNSCRIPT *runscript; alist *list; list = *item->alistvalue; @@ -3873,7 +3850,7 @@ static void print_config_cb(RES_ITEM *items, int i, POOL_MEM &cfg_str, bool hide */ int cnt = 0; RES *res; - alist* list; + alist *list; POOL_MEM res_names; list = *(items[i].alistvalue); @@ -4070,7 +4047,7 @@ static void print_config_cb(RES_ITEM *items, int i, POOL_MEM &cfg_str, bool hide */ int cnt = 0; char *audit_event; - alist* list; + alist *list; POOL_MEM audit_events; list = *(items[i].alistvalue); diff --git a/src/dird/inc_conf.c b/src/dird/inc_conf.c index c7c4e0d749f..37a292702b2 100644 --- a/src/dird/inc_conf.c +++ b/src/dird/inc_conf.c @@ -815,14 +815,14 @@ void store_inc(LEX *lc, RES_ITEM *item, int index, int pass) scan_err0(lc, _("Old style Include/Exclude not supported\n")); } -bool print_incexc_schema_json(POOL_MEM &buffer, int level, - const int type, const bool last) +#ifdef HAVE_JANSSON +json_t *json_incexc(const int type) { - return print_datatype_schema_json(buffer, level, type, newinc_items, last); + return json_datatype(type, newinc_items); } -bool print_options_schema_json(POOL_MEM &buffer, int level, - const int type, const bool last) +json_t *json_options(const int type) { - return print_datatype_schema_json(buffer, level, type, options_items, last); + return json_datatype(type, options_items); } +#endif diff --git a/src/dird/protos.h b/src/dird/protos.h index 628cfd394fe..190775d4876 100644 --- a/src/dird/protos.h +++ b/src/dird/protos.h @@ -81,6 +81,9 @@ bool despool_attributes_from_file(JCR *jcr, const char *file); /* dird_conf.c */ bool print_datatype_schema_json(POOL_MEM &buffer, int level, const int type, RES_ITEM items[], const bool last = false); +#ifdef HAVE_JANSSON +json_t *json_datatype(const int type, RES_ITEM items[]); +#endif const char *auth_protocol_to_str(uint32_t auth_protocol); const char *level_to_str(int level); extern "C" char *job_code_callback_director(JCR *jcr, const char*); @@ -118,6 +121,11 @@ bool print_incexc_schema_json(POOL_MEM &buffer, int level, const int type, const bool last = false); bool print_options_schema_json(POOL_MEM &buffer, int level, const int type, const bool last = false); +#ifdef HAVE_JANSSON +json_t *json_incexc(const int type); +json_t *json_options(const int type); +#endif + /* job.c */ bool allow_duplicate_job(JCR *jcr); diff --git a/src/dird/sd_cmds.c b/src/dird/sd_cmds.c index 1f134be5bef..d09bad00102 100644 --- a/src/dird/sd_cmds.c +++ b/src/dird/sd_cmds.c @@ -773,7 +773,7 @@ void do_native_storage_status(UAContext *ua, STORERES *store, char *cmd) ua->send_msg("%s", sd->msg); } - sd->signal( BNET_TERMINATE); + sd->signal(BNET_TERMINATE); sd->close(); delete ua->jcr->store_bsock; ua->jcr->store_bsock = NULL; diff --git a/src/dird/ua.h b/src/dird/ua.h index 56574450c23..4d5d4794536 100644 --- a/src/dird/ua.h +++ b/src/dird/ua.h @@ -3,7 +3,7 @@ Copyright (C) 2001-2011 Free Software Foundation Europe e.V. Copyright (C) 2011-2012 Planets Communications B.V. - Copyright (C) 2013-2013 Bareos GmbH & Co. KG + Copyright (C) 2013-2015 Bareos GmbH & Co. KG This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public @@ -63,6 +63,7 @@ class UAContext { uint32_t pint32_val; /* Positive integer */ int32_t int32_val; /* Positive/negative */ int64_t int64_val; /* Big int */ + OUTPUT_FORMATTER *send; /* object instance to handle output */ void signal(int sig) { UA_sock->signal(sig); }; diff --git a/src/dird/ua_cmds.c b/src/dird/ua_cmds.c index 8eedb08bfa4..afe7a888428 100644 --- a/src/dird/ua_cmds.c +++ b/src/dird/ua_cmds.c @@ -145,7 +145,7 @@ static struct cmdstruct commands[] = { "\tfiles jobid= | files ujobid= | pools | jobtotals |\n" "\tvolumes [ jobid= ujobid= pool= ] |\n" "\tmedia [ jobid= ujobid= pool= ] | clients |\n" - "\tnextvol job= | nextvolume ujobid= | copies jobid="), true, true }, + "\tnextvol job= | nextvolume ujobid= | copies jobid= [ limit= ]"), true, true }, { NT_("llist"), llist_cmd, _("Full or long list like list command"), NT_("jobs | jobid= | ujobid= | job= | jobmedia jobid= |\n" "\tjobmedia ujobid= | joblog jobid= | joblog ujobid= |\n" @@ -202,7 +202,7 @@ static struct cmdstruct commands[] = { { NT_("status"), status_cmd, _("Report status"), NT_("all | dir= | director | scheduler | schedule= | client= |\n" "\tstorage= slots | days= | job= | schedule= |\n" - "\tsubscriptions" ), true, true }, + "\tsubscriptions"), true, true }, { NT_("setbandwidth"), setbwlimit_cmd, _("Sets bandwidth"), NT_("client= | storage= | jobid= |\n" "\tjob= | ujobid= state= | all\n" @@ -298,7 +298,9 @@ bool do_a_command(UAContext *ua) if (ua->api) { user->signal(BNET_CMD_BEGIN); } + ua->send->set_mode(ua->api); ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */ + ua->send->finalize_result(ok); if (ua->api) { user->signal(ok ? BNET_CMD_OK : BNET_CMD_FAILED); } @@ -2389,22 +2391,29 @@ static int help_cmd(UAContext *ua, const char *cmd) { int i; - ua->send_msg(_(" Command Description\n ======= ===========\n")); + ua->send->decoration("%s", _(" Command Description\n ======= ===========\n")); for (i = 0; i < comsize; i++) { if (ua->argc == 2) { if (bstrcasecmp(ua->argk[1], commands[i].key)) { - ua->send_msg(_(" %-13s %s\n\nArguments:\n\t%s\n"), commands[i].key, - commands[i].help, commands[i].usage); + ua->send->object_start(commands[i].key); + ua->send->object_key_value("command", commands[i].key, " %-13s"); + ua->send->object_key_value("description", commands[i].help, " %s\n\n"); + ua->send->object_key_value("arguments", commands[i].usage, "Arguments:\n\t%s\n"); + ua->send->object_end(commands[i].key); break; } } else { - ua->send_msg(_(" %-13s %s\n"), commands[i].key, commands[i].help); + ua->send->object_start(commands[i].key); + ua->send->object_key_value("command", commands[i].key, " %-13s"); + ua->send->object_key_value("description", commands[i].help, " %s\n"); + ua->send->object_key_value("arguments", commands[i].usage); + ua->send->object_end(commands[i].key); } } if (i == comsize && ua->argc == 2) { ua->send_msg(_("\nCan't find %s command.\n\n"), ua->argk[1]); } - ua->send_msg(_("\nWhen at a prompt, entering a period cancels the command.\n\n")); + ua->send->decoration(_("\nWhen at a prompt, entering a period cancels the command.\n\n")); return 1; } @@ -2612,7 +2621,7 @@ bool open_db(UAContext *ua, bool use_private) if (!ua->catalog) { ua->catalog = get_catalog_resource(ua); if (!ua->catalog) { - ua->error_msg( _("Could not find a Catalog resource\n")); + ua->error_msg(_("Could not find a Catalog resource\n")); return false; } } diff --git a/src/dird/ua_dotcmds.c b/src/dird/ua_dotcmds.c index 1552ded62ba..cd41886d561 100644 --- a/src/dird/ua_dotcmds.c +++ b/src/dird/ua_dotcmds.c @@ -3,7 +3,7 @@ Copyright (C) 2002-2011 Free Software Foundation Europe e.V. Copyright (C) 2011-2012 Planets Communications B.V. - Copyright (C) 2013-2014 Bareos GmbH & Co. KG + Copyright (C) 2013-2015 Bareos GmbH & Co. KG This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public @@ -89,47 +89,50 @@ static int one_handler(void *ctx, int num_field, char **row); struct cmdstruct { const char *key; bool (*func)(UAContext *ua, const char *cmd); - const char *help; /* Help */ - const bool use_in_rs; /* Can be used in runscript */ + const char *help; /* Help */ + const char *usage; /* All arguments to build usage */ + const bool use_in_rs; /* Can be used in runscript */ const bool audit_event; /* Log an audit event when this Command is executed */ }; static struct cmdstruct commands[] = { - { NT_(".api"), api_cmd, NULL, false, false }, - { NT_(".backups"), backupscmd, NULL, false, false }, - { NT_(".clients"), clientscmd, NULL, true, false }, - { NT_(".catalogs"), catalogscmd, NULL, false, false }, - { NT_(".defaults"), defaultscmd, NULL, false, false }, - { NT_(".die"), admin_cmds, NULL, false, true }, - { NT_(".dump"), admin_cmds, NULL, false, true }, - { NT_(".exit"), admin_cmds, NULL, false, false }, - { NT_(".filesets"), filesetscmd, NULL, false, false }, - { NT_(".help"), dot_help_cmd, NULL, false, false }, - { NT_(".jobdefs"), jobdefscmd, NULL, true, false }, - { NT_(".jobs"), jobscmd, NULL, true, false }, - { NT_(".levels"), levelscmd, NULL, false, false }, - { NT_(".messages"), getmsgscmd, NULL, false, false }, - { NT_(".msgs"), msgscmd, NULL, false, false }, - { NT_(".pools"), poolscmd, NULL, true, false }, - { NT_(".quit"), dot_quit_cmd, NULL, false, false }, - { NT_(".sql"), sql_cmd, NULL, false, true }, - { NT_(".schedule"), schedulecmd, NULL, false, false }, - { NT_(".status"), dot_status_cmd, NULL, false, true }, - { NT_(".storage"), storagecmd, NULL, true, false }, - { NT_(".volstatus"), volstatuscmd, NULL, true, false }, - { NT_(".media"), mediacmd, NULL, true, false }, - { NT_(".mediatypes"), mediatypescmd, NULL, true, false }, - { NT_(".locations"), locationscmd, NULL, true, false }, - { NT_(".profiles"), profilescmd, NULL, true, false }, - { NT_(".actiononpurge"), aopcmd, NULL, true, false }, - { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs, NULL, true, true }, - { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles, NULL, true, true }, - { NT_(".bvfs_update"), dot_bvfs_update, NULL, true, true }, - { NT_(".bvfs_get_jobids"), dot_bvfs_get_jobids, NULL, true, true }, - { NT_(".bvfs_versions"), dot_bvfs_versions, NULL, true, true }, - { NT_(".bvfs_restore"), dot_bvfs_restore, NULL, true, true }, - { NT_(".bvfs_cleanup"), dot_bvfs_cleanup, NULL, true, true }, - { NT_(".bvfs_clear_cache"), dot_bvfs_clear_cache, NULL, false, true }, - { NT_(".types"), typescmd, NULL, false, false } + { NT_(".api"), api_cmd, _("Switch between different api modes"), + NT_("0 | 1 | 2 | off | json"), false, false }, + { NT_(".backups"), backupscmd, NULL, NULL, false, false }, + { NT_(".clients"), clientscmd, NULL, NULL, true, false }, + { NT_(".catalogs"), catalogscmd, NULL, NULL, false, false }, + { NT_(".defaults"), defaultscmd, NULL, NULL, false, false }, + { NT_(".die"), admin_cmds, NULL, NULL, false, true }, + { NT_(".dump"), admin_cmds, NULL, NULL, false, true }, + { NT_(".exit"), admin_cmds, NULL, NULL, false, false }, + { NT_(".filesets"), filesetscmd, NULL, NULL, false, false }, + { NT_(".help"), dot_help_cmd, NULL, NULL, false, false }, + { NT_(".jobdefs"), jobdefscmd, NULL, NULL, true, false }, + { NT_(".jobs"), jobscmd, NULL, + NT_("type="), true, false }, + { NT_(".levels"), levelscmd, NULL, NULL, false, false }, + { NT_(".messages"), getmsgscmd, NULL, NULL, false, false }, + { NT_(".msgs"), msgscmd, NULL, NULL, false, false }, + { NT_(".pools"), poolscmd, NULL, NULL, true, false }, + { NT_(".quit"), dot_quit_cmd, NULL, NULL, false, false }, + { NT_(".sql"), sql_cmd, NULL, NULL, false, true }, + { NT_(".schedule"), schedulecmd, NULL, NULL, false, false }, + { NT_(".status"), dot_status_cmd, NULL, NULL, false, true }, + { NT_(".storage"), storagecmd, NULL, NULL, true, false }, + { NT_(".volstatus"), volstatuscmd, NULL, NULL, true, false }, + { NT_(".media"), mediacmd, NULL, NULL, true, false }, + { NT_(".mediatypes"), mediatypescmd, NULL, NULL, true, false }, + { NT_(".locations"), locationscmd, NULL, NULL, true, false }, + { NT_(".profiles"), profilescmd, NULL, NULL, true, false }, + { NT_(".actiononpurge"), aopcmd, NULL, NULL, true, false }, + { NT_(".bvfs_lsdirs"), dot_bvfs_lsdirs, NULL, NULL, true, true }, + { NT_(".bvfs_lsfiles"),dot_bvfs_lsfiles, NULL, NULL, true, true }, + { NT_(".bvfs_update"), dot_bvfs_update, NULL, NULL, true, true }, + { NT_(".bvfs_get_jobids"), dot_bvfs_get_jobids, NULL, NULL, true, true }, + { NT_(".bvfs_versions"), dot_bvfs_versions, NULL, NULL, true, true }, + { NT_(".bvfs_restore"), dot_bvfs_restore, NULL, NULL, true, true }, + { NT_(".bvfs_cleanup"), dot_bvfs_cleanup, NULL, NULL, true, true }, + { NT_(".bvfs_clear_cache"), dot_bvfs_clear_cache, NULL, NULL, false, true }, + { NT_(".types"), typescmd, NULL, NULL, false, false } }; #define comsize ((int)(sizeof(commands)/sizeof(struct cmdstruct))) @@ -194,7 +197,9 @@ bool do_a_dot_command(UAContext *ua) if (ua->api) { user->signal(BNET_CMD_BEGIN); } + ua->send->set_mode(ua->api); ok = (*commands[i].func)(ua, ua->cmd); /* go execute command */ + ua->send->finalize_result(ok); if (ua->api) { user->signal(ok?BNET_CMD_OK:BNET_CMD_FAILED); } @@ -251,11 +256,34 @@ static bool dot_bvfs_clear_cache(UAContext *ua, const char *cmd) return true; } -static int bvfs_result_handler(void *ctx, int fields, char **row) +static int bvfs_stat(UAContext *ua, char *lstat) { - UAContext *ua = (UAContext *)ctx; struct stat statp; int32_t LinkFI; + + memset(&statp, 0, sizeof(struct stat)); + decode_stat(lstat, &statp, sizeof(statp), &LinkFI); + + ua->send->object_start("stat"); + ua->send->object_key_value("dev", statp.st_dev); + ua->send->object_key_value("ino", statp.st_ino); + ua->send->object_key_value("mode", statp.st_mode); + ua->send->object_key_value("nlink", statp.st_nlink); + ua->send->object_key_value("uid", statp.st_uid); + ua->send->object_key_value("gid", statp.st_gid); + ua->send->object_key_value("rdev", statp.st_rdev); + ua->send->object_key_value("size", statp.st_size); + ua->send->object_key_value("atime", statp.st_atime); + ua->send->object_key_value("mtime", statp.st_mtime); + ua->send->object_key_value("ctime", statp.st_ctime); + ua->send->object_end("stat"); + + return 0; +} + +static int bvfs_result_handler(void *ctx, int fields, char **row) +{ + UAContext *ua = (UAContext *)ctx; char *fileid=row[BVFS_FileId]; char *lstat=row[BVFS_LStat]; char *jobid=row[BVFS_JobId]; @@ -270,25 +298,43 @@ static int bvfs_result_handler(void *ctx, int fields, char **row) fileid = zero; } - memset(&statp, 0, sizeof(struct stat)); - decode_stat(lstat, &statp, sizeof(statp), &LinkFI); - Dmsg1(100, "type=%s\n", row[0]); if (bvfs_is_dir(row)) { char *path = bvfs_basename_dir(row[BVFS_Name]); - ua->send_msg("%s\t0\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], fileid, - jobid, lstat, path); - - } else if (bvfs_is_version(row)) { - ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], - row[BVFS_FilenameId], fileid, jobid, - lstat, row[BVFS_Md5], row[BVFS_VolName], - row[BVFS_VolInchanger]); - + ua->send->object_start(row[BVFS_Name]); + ua->send->object_key_value("Type", row[BVFS_Type]); + ua->send->object_key_value("PathId", str_to_uint64(row[BVFS_PathId]), "%lld\t"); + ua->send->object_key_value("FilenameId", FileId_t(0), "%lld\t"); + ua->send->object_key_value("FileId", str_to_uint64(fileid), "%lld\t"); + ua->send->object_key_value("JobId", str_to_uint64(jobid), "%lld\t"); + ua->send->object_key_value("lstat", lstat, "%s\t"); + ua->send->object_key_value("Name", path, "%s\n"); + ua->send->object_key_value("Fullpath", row[BVFS_Name]); + bvfs_stat(ua, lstat); + ua->send->object_end(row[BVFS_Name]); + } else if (bvfs_is_version(row)) { + ua->send->object_start(row[BVFS_Name]); + ua->send->object_key_value("Type", row[BVFS_Type]); + ua->send->object_key_value("PathId", str_to_uint64(row[BVFS_PathId]), "%lld\t"); + ua->send->object_key_value("FilenameId", str_to_uint64(row[BVFS_FilenameId]), "%lld\t"); + ua->send->object_key_value("FileId", str_to_uint64(fileid), "%lld\t"); + ua->send->object_key_value("JobId", str_to_uint64(jobid), "%lld\t"); + ua->send->object_key_value("lstat", lstat, "%s\t"); + ua->send->object_key_value("MD5", row[BVFS_Md5], "%s\t"); + ua->send->object_key_value("VolumeName", row[BVFS_VolName], "%s\t"); + ua->send->object_key_value("VolumeInChanger", str_to_uint64(row[BVFS_VolInchanger]), "%lld\n"); + ua->send->object_end(row[BVFS_Name]); } else if (bvfs_is_file(row)) { - ua->send_msg("%s\t%s\t%s\t%s\t%s\t%s\n", row[BVFS_PathId], - row[BVFS_FilenameId], fileid, jobid, - lstat, row[BVFS_Name]); + ua->send->object_start(row[BVFS_Name]); + ua->send->object_key_value("Type", row[BVFS_Type]); + ua->send->object_key_value("PathId", str_to_uint64(row[BVFS_PathId]), "%lld\t"); + ua->send->object_key_value("FilenameId", str_to_uint64(row[BVFS_FilenameId]), "%lld\t"); + ua->send->object_key_value("FileId", str_to_uint64(fileid), "%lld\t"); + ua->send->object_key_value("JobId", str_to_uint64(jobid), "%lld\t"); + ua->send->object_key_value("lstat", lstat, "%s\t"); + ua->send->object_key_value("Name", row[BVFS_Name], "%s\n"); + bvfs_stat(ua, lstat); + ua->send->object_end(row[BVFS_Name]); } return 0; @@ -343,7 +389,7 @@ static bool bvfs_parse_arg(UAContext *ua, *jobid = NULL; *username = NULL; - for (int i=1; iargc; i++) { + for (int i = 1; i < ua->argc; i++) { if (bstrcasecmp(ua->argk[i], NT_("pathid"))) { if (is_a_number(ua->argv[i])) { *pathid = str_to_int64(ua->argv[i]); @@ -611,7 +657,7 @@ static bool dot_bvfs_get_jobids(UAContext *ua, const char *cmd) jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */ /* Foreach different FileSet, we build a restore jobid list */ - for (int i=0; i < ids.num_ids; i++) { + for (int i = 0; i < ids.num_ids; i++) { jr.FileSetId = ids.DBId[i]; if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &tempids)) { return true; @@ -728,7 +774,7 @@ static bool admin_cmds(UAContext *ua, const char *cmd) return true; } /* General debug? */ - for (i=1; iargc; i++) { + for (i = 1; i < ua->argc; i++) { if (bstrcasecmp(ua->argk[i], "dir") || bstrcasecmp(ua->argk[i], "director")) { dir = true; @@ -835,11 +881,15 @@ static bool jobdefscmd(UAContext *ua, const char *cmd) JOBRES *jobdefs; LockRes(); + ua->send->object_start(); foreach_res(jobdefs, R_JOBDEFS) { if (acl_access_ok(ua, Job_ACL, jobdefs->name())) { - ua->send_msg("%s\n", jobdefs->name()); + ua->send->object_start("jobdefs"); + ua->send->object_key_value("name", jobdefs->name(), "%s\n"); + ua->send->object_end("jobdefs"); } } + ua->send->object_end(); UnlockRes(); return true; @@ -859,13 +909,17 @@ static bool jobscmd(UAContext *ua, const char *cmd) type = ua->argv[pos][0]; } LockRes(); + ua->send->object_start(); foreach_res(job, R_JOB) { if (!type || type == job->JobType) { if (acl_access_ok(ua, Job_ACL, job->name())) { - ua->send_msg("%s\n", job->name()); + ua->send->object_start("jobs"); + ua->send->object_key_value("name", job->name(), "%s\n"); + ua->send->object_end("jobs"); } } } + ua->send->object_end(); UnlockRes(); return true; @@ -876,11 +930,15 @@ static bool filesetscmd(UAContext *ua, const char *cmd) FILESETRES *fs; LockRes(); + ua->send->object_start(); foreach_res(fs, R_FILESET) { if (acl_access_ok(ua, FileSet_ACL, fs->name())) { - ua->send_msg("%s\n", fs->name()); + ua->send->object_start("filesets"); + ua->send->object_key_value("name", fs->name(), "%s\n"); + ua->send->object_end("filesets"); } } + ua->send->object_end(); UnlockRes(); return true; @@ -891,11 +949,15 @@ static bool catalogscmd(UAContext *ua, const char *cmd) CATRES *cat; LockRes(); + ua->send->object_start(); foreach_res(cat, R_CATALOG) { if (acl_access_ok(ua, Catalog_ACL, cat->name())) { - ua->send_msg("%s\n", cat->name()); + ua->send->object_start("catalogs"); + ua->send->object_key_value("name", cat->name(), "%s\n"); + ua->send->object_end("catalogs"); } } + ua->send->object_end(); UnlockRes(); return true; @@ -906,11 +968,15 @@ static bool clientscmd(UAContext *ua, const char *cmd) CLIENTRES *client; LockRes(); + ua->send->object_start(); foreach_res(client, R_CLIENT) { if (acl_access_ok(ua, Client_ACL, client->name())) { - ua->send_msg("%s\n", client->name()); + ua->send->object_start("clients"); + ua->send->object_key_value("name", client->name(), "%s\n"); + ua->send->object_end("clients"); } } + ua->send->object_end(); UnlockRes(); return true; @@ -921,9 +987,13 @@ static bool msgscmd(UAContext *ua, const char *cmd) MSGSRES *msgs = NULL; LockRes(); + ua->send->object_start("messages"); foreach_res(msgs, R_MSGS) { - ua->send_msg("%s\n", msgs->name()); + ua->send->object_start(); + ua->send->object_key_value("text", msgs->name(), "%s\n"); + ua->send->object_end(); } + ua->send->object_end("messages"); UnlockRes(); return true; @@ -934,11 +1004,15 @@ static bool poolscmd(UAContext *ua, const char *cmd) POOLRES *pool; LockRes(); + ua->send->object_start(); foreach_res(pool, R_POOL) { if (acl_access_ok(ua, Pool_ACL, pool->name())) { - ua->send_msg("%s\n", pool->name()); + ua->send->object_start("pools"); + ua->send->object_key_value("name", pool->name(), "%s\n"); + ua->send->object_end("pools"); } } + ua->send->object_end(); UnlockRes(); return true; @@ -949,11 +1023,15 @@ static bool storagecmd(UAContext *ua, const char *cmd) STORERES *store; LockRes(); + ua->send->object_start(); foreach_res(store, R_STORAGE) { if (acl_access_ok(ua, Storage_ACL, store->name())) { - ua->send_msg("%s\n", store->name()); + ua->send->object_start("storages"); + ua->send->object_key_value("name", store->name(), "%s\n"); + ua->send->object_end("storages"); } } + ua->send->object_end(); UnlockRes(); return true; @@ -964,9 +1042,13 @@ static bool profilescmd(UAContext *ua, const char *cmd) PROFILERES *profile; LockRes(); + ua->send->object_start(); foreach_res(profile, R_PROFILE) { - ua->send_msg("%s\n", profile->name()); + ua->send->object_start("profiles"); + ua->send->object_key_value("name", profile->name(), "%s\n"); + ua->send->object_end("profiles"); } + ua->send->object_end(); UnlockRes(); return true; @@ -974,21 +1056,27 @@ static bool profilescmd(UAContext *ua, const char *cmd) static bool aopcmd(UAContext *ua, const char *cmd) { - int i; - - for (i = 0; ActionOnPurgeOptions[i].name; i++) { - ua->send_msg("%s\n", ActionOnPurgeOptions[i].name); + ua->send->object_start(); + for (int i = 0; ActionOnPurgeOptions[i].name; i++) { + ua->send->object_start("actiononpurge"); + ua->send->object_key_value("name", ActionOnPurgeOptions[i].name, "%s\n"); + ua->send->object_end("actionsonpurge"); } + ua->send->object_end(); + return true; } static bool typescmd(UAContext *ua, const char *cmd) { - int i; - - for (i = 0; jobtypes[i].type_name; i++) { - ua->send_msg("%s\n", jobtypes[i].type_name); + ua->send->object_start(); + for (int i = 0; jobtypes[i].type_name; i++) { + ua->send->object_start("jobtypes"); + ua->send->object_key_value("name", jobtypes[i].type_name, "%s\n"); + ua->send->object_end("jobtypes"); } + ua->send->object_end(); + return true; } @@ -1002,11 +1090,31 @@ static bool typescmd(UAContext *ua, const char *cmd) */ static bool api_cmd(UAContext *ua, const char *cmd) { + int value = 0; + if (ua->argc == 2) { - ua->api = atoi(ua->argk[1]); + if (bstrcasecmp(ua->argk[1], "off")) { + ua->api = API_MODE_OFF; + } else if (bstrcasecmp(ua->argk[1], "on")) { + ua->api = API_MODE_ON; + } else if (bstrcasecmp(ua->argk[1], "json")) { + ua->api = API_MODE_JSON; + } else { + value = atoi(ua->argk[1]); + if ((value < API_MODE_OFF) || (value > API_MODE_JSON)) { + return false; + } + ua->api = value; + } } else { ua->api = 1; } + + ua->send->set_mode(ua->api); + ua->send->object_start(); + ua->send->object_key_value("api", "%s: ", ua->api, "%d\n"); + ua->send->object_end(); + return true; } @@ -1058,7 +1166,7 @@ static int sql_handler(void *ctx, int num_field, char **row) if (num_field == 0 || row == NULL || row[0] == NULL) { return 0; /* nothing returned */ } - for (int i=0; num_field--; i++) { + for (int i = 0; num_field--; i++) { if (i == 0) { pm_strcpy(rows, NPRT(row[0])); } else { @@ -1100,7 +1208,11 @@ static bool sql_cmd(UAContext *ua, const char *cmd) static int one_handler(void *ctx, int num_field, char **row) { UAContext *ua = (UAContext *)ctx; - ua->send_msg("%s\n", row[0]); + + ua->send->object_start(); + ua->send->object_key_value("name", row[0], "%s\n"); + ua->send->object_end(); + return 0; } @@ -1109,12 +1221,15 @@ static bool mediatypescmd(UAContext *ua, const char *cmd) if (!open_client_db(ua)) { return true; } + + ua->send->object_start("mediatypes"); if (!db_sql_query(ua->db, - "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType", - one_handler, (void *)ua)) - { + "SELECT DISTINCT MediaType FROM MediaType ORDER BY MediaType", + one_handler, (void *)ua)) { ua->error_msg(_("List MediaType failed: ERR=%s\n"), db_strerror(ua->db)); } + ua->send->object_end("mediatypes"); + return true; } @@ -1123,12 +1238,15 @@ static bool mediacmd(UAContext *ua, const char *cmd) if (!open_client_db(ua)) { return true; } + + ua->send->object_start("media"); if (!db_sql_query(ua->db, - "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName", - one_handler, (void *)ua)) - { + "SELECT DISTINCT Media.VolumeName FROM Media ORDER BY VolumeName", + one_handler, (void *)ua)) { ua->error_msg(_("List Media failed: ERR=%s\n"), db_strerror(ua->db)); } + ua->send->object_end("media"); + return true; } @@ -1137,10 +1255,15 @@ static bool schedulecmd(UAContext *ua, const char *cmd) SCHEDRES *sched; LockRes(); + ua->send->object_start(); foreach_res(sched, R_SCHEDULE) { - ua->send_msg("%s\n", sched->hdr.name); + ua->send->object_start("schedules"); + ua->send->object_key_value("name", sched->hdr.name, "%s\n"); + ua->send->object_end("schedules"); } + ua->send->object_end(); UnlockRes(); + return true; } @@ -1149,26 +1272,32 @@ static bool locationscmd(UAContext *ua, const char *cmd) if (!open_client_db(ua)) { return true; } + + ua->send->object_start("locations"); if (!db_sql_query(ua->db, - "SELECT DISTINCT Location FROM Location ORDER BY Location", - one_handler, (void *)ua)) - { + "SELECT DISTINCT Location FROM Location ORDER BY Location", + one_handler, (void *)ua)) { ua->error_msg(_("List Location failed: ERR=%s\n"), db_strerror(ua->db)); } + ua->send->object_end("locations"); + return true; } static bool levelscmd(UAContext *ua, const char *cmd) { - int i; - /* * Note some levels are blank, which means none is needed */ + ua->send->object_start(); if (ua->argc == 1) { - for (i=0; joblevels[i].level_name; i++) { + for (int i = 0; joblevels[i].level_name; i++) { if (joblevels[i].level_name[0] != ' ') { - ua->send_msg("%s\n", joblevels[i].level_name); + ua->send->object_start("levels"); + ua->send->object_key_value("name", joblevels[i].level_name, "%s\n"); + ua->send->object_key_value("level", joblevels[i].level); + ua->send->object_key_value("jobtype", joblevels[i].job_type); + ua->send->object_end("levels"); } } } else if (ua->argc == 2) { @@ -1177,29 +1306,38 @@ static bool levelscmd(UAContext *ua, const char *cmd) /* * Assume that first argument is the Job Type */ - for (i=0; jobtypes[i].type_name; i++) { + for (int i = 0; jobtypes[i].type_name; i++) { if (bstrcasecmp(ua->argk[1], jobtypes[i].type_name)) { jobtype = jobtypes[i].job_type; break; } } - for (i=0; joblevels[i].level_name; i++) { + + for (int i = 0; joblevels[i].level_name; i++) { if ((joblevels[i].job_type == jobtype) && (joblevels[i].level_name[0] != ' ')) { - ua->send_msg("%s\n", joblevels[i].level_name); + ua->send->object_start("levels"); + ua->send->object_key_value("name", joblevels[i].level_name, "%s\n"); + ua->send->object_key_value("level", joblevels[i].level); + ua->send->object_key_value("jobtype", joblevels[i].job_type); + ua->send->object_end("levels"); } } } + ua->send->object_end(); return true; } static bool volstatuscmd(UAContext *ua, const char *cmd) { - int i; - - for (i = 0; VolumeStatus[i].name; i++) { - ua->send_msg("%s\n", VolumeStatus[i].name); + ua->send->object_start(); + for (int i = 0; VolumeStatus[i].name; i++) { + ua->send->object_start("volstatus"); + ua->send->object_key_value("name", VolumeStatus[i].name, "%s\n"); + ua->send->object_end("volstatus"); } + ua->send->object_end(); + return true; } diff --git a/src/dird/ua_output.c b/src/dird/ua_output.c index cb9dd4477e7..de336175594 100644 --- a/src/dird/ua_output.c +++ b/src/dird/ua_output.c @@ -3,7 +3,7 @@ Copyright (C) 2000-2012 Free Software Foundation Europe e.V. Copyright (C) 2011-2012 Planets Communications B.V. - Copyright (C) 2013-2014 Bareos GmbH & Co. KG + Copyright (C) 2013-2015 Bareos GmbH & Co. KG This program is Free Software; you can redistribute it and/or modify it under the terms of version three of the GNU Affero General Public @@ -31,6 +31,10 @@ #include "bareos.h" #include "dird.h" +#if HAVE_JANSSON +#define UA_JSON_FLAGS JSON_INDENT(2) +#endif + /* Imported subroutines */ /* Imported variables */ @@ -391,11 +395,11 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) for (i = 1; i < ua->argc; i++) { /* List JOBS */ if (bstrcasecmp(ua->argk[i], NT_("jobs"))) { - db_list_job_records(ua->jcr, ua->db, &jr, printit, ua, llist); + db_list_job_records(ua->jcr, ua->db, &jr, ua->send, llist); /* List JOBTOTALS */ } else if (bstrcasecmp(ua->argk[i], NT_("jobtotals"))) { - db_list_job_totals(ua->jcr, ua->db, &jr, printit, ua); + db_list_job_totals(ua->jcr, ua->db, &jr, ua->send); /* List JOBID=nn */ } else if (bstrcasecmp(ua->argk[i], NT_("jobid"))) { @@ -403,7 +407,7 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) jobid = str_to_int64(ua->argv[i]); if (jobid > 0) { jr.JobId = jobid; - db_list_job_records(ua->jcr, ua->db, &jr, printit, ua, llist); + db_list_job_records(ua->jcr, ua->db, &jr, ua->send, llist); } } @@ -412,13 +416,13 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) bstrcasecmp(ua->argk[i], NT_("jobname"))) && ua->argv[i]) { bstrncpy(jr.Name, ua->argv[i], MAX_NAME_LENGTH); jr.JobId = 0; - db_list_job_records(ua->jcr, ua->db, &jr, printit, ua, llist); + db_list_job_records(ua->jcr, ua->db, &jr, ua->send, llist); /* List UJOBID=xxx */ } else if (bstrcasecmp(ua->argk[i], NT_("ujobid")) && ua->argv[i]) { bstrncpy(jr.Job, ua->argv[i], MAX_NAME_LENGTH); jr.JobId = 0; - db_list_job_records(ua->jcr, ua->db, &jr, printit, ua, llist); + db_list_job_records(ua->jcr, ua->db, &jr, ua->send, llist); /* List Base files */ } else if (bstrcasecmp(ua->argk[i], NT_("basefiles"))) { @@ -436,7 +440,7 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) continue; } if (jobid > 0) { - db_list_base_files_for_job(ua->jcr, ua->db, jobid, printit, ua); + db_list_base_files_for_job(ua->jcr, ua->db, jobid, ua->send); } } @@ -457,7 +461,7 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) continue; } if (jobid > 0) { - db_list_files_for_job(ua->jcr, ua->db, jobid, printit, ua); + db_list_files_for_job(ua->jcr, ua->db, jobid, ua->send); } } @@ -477,12 +481,12 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) } else { continue; } - db_list_jobmedia_records(ua->jcr, ua->db, jobid, printit, ua, llist); + db_list_jobmedia_records(ua->jcr, ua->db, jobid, ua->send, llist); done = true; } if (!done) { /* List for all jobs (jobid=0) */ - db_list_jobmedia_records(ua->jcr, ua->db, 0, printit, ua, llist); + db_list_jobmedia_records(ua->jcr, ua->db, 0, ua->send, llist); } /* List JOBLOG */ @@ -501,12 +505,12 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) } else { continue; } - db_list_joblog_records(ua->jcr, ua->db, jobid, printit, ua, llist); + db_list_joblog_records(ua->jcr, ua->db, jobid, ua->send, llist); done = true; } if (!done) { /* List for all jobs (jobid=0) */ - db_list_joblog_records(ua->jcr, ua->db, 0, printit, ua, llist); + db_list_joblog_records(ua->jcr, ua->db, 0, ua->send, llist); } @@ -518,10 +522,10 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) if (ua->argv[i]) { bstrncpy(pr.Name, ua->argv[i], sizeof(pr.Name)); } - db_list_pool_records(ua->jcr, ua->db, &pr, printit, ua, llist); + db_list_pool_records(ua->jcr, ua->db, &pr, ua->send, llist); } else if (bstrcasecmp(ua->argk[i], NT_("clients"))) { - db_list_client_records(ua->jcr, ua->db, printit, ua, llist); + db_list_client_records(ua->jcr, ua->db, ua->send, llist); /* List MEDIA or VOLUMES */ } else if (bstrcasecmp(ua->argk[i], NT_("media")) || @@ -554,7 +558,7 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) /* List a specific volume? */ if (ua->argv[i]) { bstrncpy(mr.VolumeName, ua->argv[i], sizeof(mr.VolumeName)); - db_list_media_records(ua->jcr, ua->db, &mr, printit, ua, llist); + db_list_media_records(ua->jcr, ua->db, &mr, ua->send, llist); return 1; } /* Is a specific pool wanted? */ @@ -565,7 +569,7 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) return 1; } mr.PoolId = pr.PoolId; - db_list_media_records(ua->jcr, ua->db, &mr, printit, ua, llist); + db_list_media_records(ua->jcr, ua->db, &mr, ua->send, llist); return 1; } } @@ -580,12 +584,14 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) return 1; } for (i=0; i < num_pools; i++) { + ua->send->object_start(); pr.PoolId = ids[i]; if (db_get_pool_record(ua->jcr, ua->db, &pr)) { - ua->send_msg(_("Pool: %s\n"), pr.Name); + ua->send->object_key_value("pool", pr.Name, "Pool: %s\n"); } mr.PoolId = ids[i]; - db_list_media_records(ua->jcr, ua->db, &mr, printit, ua, llist); + db_list_media_records(ua->jcr, ua->db, &mr, ua->send, llist); + ua->send->object_end(); } free(ids); return 1; @@ -615,7 +621,7 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist) limit = atoi(ua->argv[j]); } } - db_list_copies_records(ua->jcr,ua->db,limit,jobids,printit,ua,llist); + db_list_copies_records(ua->jcr, ua->db, limit, jobids, ua->send, llist); } else if (bstrcasecmp(ua->argk[i], NT_("limit")) || bstrcasecmp(ua->argk[i], NT_("days"))) { /* Ignore it */ @@ -908,14 +914,12 @@ void printit(void *ctx, const char *msg) { UAContext *ua = (UAContext *)ctx; - if (ua) { - ua->send_msg("%s", msg); - } + ua->UA_sock->fsend("%s", msg); } /* * Format message and send to other end. - * + * If the UA_sock is NULL, it means that there is no user * agent, so we are being called from BAREOS core. In * that case direct the messages to the Job. diff --git a/src/dird/ua_query.c b/src/dird/ua_query.c index 88c866208bc..592978ea4a9 100644 --- a/src/dird/ua_query.c +++ b/src/dird/ua_query.c @@ -126,8 +126,8 @@ int query_cmd(UAContext *ua, const char *cmd) query = substitute_prompts(ua, query, prompt, nprompt); Dmsg1(100, "Query2=%s\n", query); if (query[0] == '!') { - db_list_sql_query(ua->jcr, ua->db, query+1, printit, ua, false, VERT_LIST); - } else if (!db_list_sql_query(ua->jcr, ua->db, query, printit, ua, true, HORZ_LIST)) { + db_list_sql_query(ua->jcr, ua->db, query+1, ua->send, false, VERT_LIST); + } else if (!db_list_sql_query(ua->jcr, ua->db, query, ua->send, true, HORZ_LIST)) { ua->send_msg("%s\n", query); } query[0] = 0; @@ -138,8 +138,8 @@ int query_cmd(UAContext *ua, const char *cmd) query = substitute_prompts(ua, query, prompt, nprompt); Dmsg1(100, "Query2=%s\n", query); if (query[0] == '!') { - db_list_sql_query(ua->jcr, ua->db, query+1, printit, ua, false, VERT_LIST); - } else if (!db_list_sql_query(ua->jcr, ua->db, query, printit, ua, true, HORZ_LIST)) { + db_list_sql_query(ua->jcr, ua->db, query+1, ua->send, false, VERT_LIST); + } else if (!db_list_sql_query(ua->jcr, ua->db, query, ua->send, true, HORZ_LIST)) { ua->error_msg("%s\n", query); } } @@ -271,7 +271,7 @@ int sqlquery_cmd(UAContext *ua, const char *cmd) if (ua->cmd[len-1] == ';') { ua->cmd[len-1] = 0; /* zap ; */ /* Submit query */ - db_list_sql_query(ua->jcr, ua->db, query.c_str(), printit, ua, true, HORZ_LIST); + db_list_sql_query(ua->jcr, ua->db, query.c_str(), ua->send, true, HORZ_LIST); *query.c_str() = 0; /* start new query */ msg = _("Enter SQL query: "); } else { diff --git a/src/dird/ua_restore.c b/src/dird/ua_restore.c index 78a3463267a..f44be3f9bce 100644 --- a/src/dird/ua_restore.c +++ b/src/dird/ua_restore.c @@ -349,7 +349,7 @@ static void get_and_display_basejobs(UAContext *ua, RESTORE_CTX *rx) Mmsg(q, uar_print_jobs, jobids.list); ua->send_msg(_("The restore will use the following job(s) as Base\n")); - db_list_sql_query(ua->jcr, ua->db, q.c_str(), printit, ua, true, HORZ_LIST); + db_list_sql_query(ua->jcr, ua->db, q.c_str(), ua->send, true, HORZ_LIST); } pm_strcpy(rx->BaseJobIds, jobids.list); } @@ -645,7 +645,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) } gui_save = ua->jcr->gui; ua->jcr->gui = true; - db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, printit, ua, true, HORZ_LIST); + db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, ua->send, true, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; @@ -663,7 +663,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) free(fname); gui_save = ua->jcr->gui; ua->jcr->gui = true; - db_list_sql_query(ua->jcr, ua->db, rx->query, printit, ua, true, HORZ_LIST); + db_list_sql_query(ua->jcr, ua->db, rx->query, ua->send, true, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; @@ -683,7 +683,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) } gui_save = ua->jcr->gui; ua->jcr->gui = true; - db_list_sql_query(ua->jcr, ua->db, ua->cmd, printit, ua, true, HORZ_LIST); + db_list_sql_query(ua->jcr, ua->db, ua->cmd, ua->send, true, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; @@ -1455,13 +1455,13 @@ static bool select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *dat * Display a list of all copies */ db_list_copies_records(ua->jcr, ua->db, 0, rx->JobIds, - printit, ua, HORZ_LIST); + ua->send, HORZ_LIST); } /* * Display a list of Jobs selected for this restore */ - db_list_sql_query(ua->jcr, ua->db, uar_list_temp, printit, ua, true, HORZ_LIST); + db_list_sql_query(ua->jcr, ua->db, uar_list_temp, ua->send, true, HORZ_LIST); ok = true; } else { ua->warning_msg(_("No jobs found.\n")); diff --git a/src/dird/ua_run.c b/src/dird/ua_run.c index ec9c1a2d676..ee4fca241c2 100644 --- a/src/dird/ua_run.c +++ b/src/dird/ua_run.c @@ -2012,7 +2012,7 @@ static bool scan_command_line_arguments(UAContext *ua, RUN_CTX &rc) if (!rc.job) { return false; } else if (!acl_access_ok(ua, Job_ACL, rc.job->name(), true)) { - ua->error_msg( _("No authorization. Job \"%s\".\n"), rc.job->name()); + ua->error_msg(_("No authorization. Job \"%s\".\n"), rc.job->name()); return false; } diff --git a/src/dird/ua_select.c b/src/dird/ua_select.c index 5296acb95e7..d0a96ba5d51 100644 --- a/src/dird/ua_select.c +++ b/src/dird/ua_select.c @@ -674,7 +674,7 @@ int select_media_dbr(UAContext *ua, MEDIA_DBR *mr) goto bail_out; } mr->PoolId = pr.PoolId; - db_list_media_records(ua->jcr, ua->db, mr, printit, ua, HORZ_LIST); + db_list_media_records(ua->jcr, ua->db, mr, ua->send, HORZ_LIST); if (!get_cmd(ua, _("Enter *MediaId or Volume name: "))) { goto bail_out; } @@ -750,7 +750,7 @@ POOLRES *get_pool_resource(UAContext *ua) */ int select_job_dbr(UAContext *ua, JOB_DBR *jr) { - db_list_job_records(ua->jcr, ua->db, jr, printit, ua, HORZ_LIST); + db_list_job_records(ua->jcr, ua->db, jr, ua->send, HORZ_LIST); if (!get_pint(ua, _("Enter the JobId to select: "))) { return 0; } diff --git a/src/dird/ua_server.c b/src/dird/ua_server.c index 60d8c28f9f9..1fbe53e88b8 100644 --- a/src/dird/ua_server.c +++ b/src/dird/ua_server.c @@ -155,6 +155,8 @@ UAContext *new_ua_context(JCR *jcr) ua->errmsg = get_pool_memory(PM_FNAME); ua->verbose = true; ua->automount = true; + ua->send = New(OUTPUT_FORMATTER(printit, ua)); + return ua; } @@ -172,6 +174,9 @@ void free_ua_context(UAContext *ua) if (ua->prompt) { free(ua->prompt); } + if (ua->send) { + delete ua->send; + } if (ua->UA_sock) { ua->UA_sock->close(); ua->UA_sock = NULL; diff --git a/src/dird/ua_update.c b/src/dird/ua_update.c index 6b655e59420..99843f5c651 100644 --- a/src/dird/ua_update.c +++ b/src/dird/ua_update.c @@ -399,7 +399,8 @@ static void update_vol_from_pool(UAContext *ua, MEDIA_DBR *mr) } /* - * Refresh the Volume information from the Pool record for all Volumes + * Refresh the Volume information from the Pool record + * for all Volumes */ static void update_all_vols_from_pool(UAContext *ua, const char *pool_name) { @@ -440,25 +441,19 @@ static void update_all_vols(UAContext *ua) for (i = 0; ijcr, ua->db, &pr)) { + if (!db_get_pool_record(ua->jcr, ua->db, &pr)) { /* ***FIXME*** use acl? */ ua->warning_msg(_("Updating all pools, but skipped PoolId=%d. ERR=%s\n"), db_strerror(ua->db)); continue; } - /* - * Check access to pool. - */ - if (!acl_access_ok(ua, Pool_ACL, pr.Name, false)) { - continue; - } - set_pool_dbr_defaults_in_media_dbr(&mr, &pr); mr.PoolId = pr.PoolId; if (!db_update_media_defaults(ua->jcr, ua->db, &mr)) { ua->error_msg(_("Error updating Volume records: ERR=%s"), db_strerror(ua->db)); } else { - ua->info_msg(_("All Volume defaults updated from \"%s\" Pool record.\n"), pr.Name); + ua->info_msg(_("All Volume defaults updated from \"%s\" Pool record.\n"), + pr.Name); } } @@ -899,7 +894,7 @@ static bool update_pool(UAContext *ua) } query = get_pool_memory(PM_MESSAGE); Mmsg(query, list_pool, edit_int64(pr.PoolId, ed1)); - db_list_sql_query(ua->jcr, ua->db, query, printit, ua, true, HORZ_LIST); + db_list_sql_query(ua->jcr, ua->db, query, ua->send, true, HORZ_LIST); free_pool_memory(query); ua->info_msg(_("Pool DB record updated from resource.\n")); return true; diff --git a/src/filed/Makefile.in b/src/filed/Makefile.in index da5f0f3fa74..e1e4fbb7d56 100644 --- a/src/filed/Makefile.in +++ b/src/filed/Makefile.in @@ -24,6 +24,7 @@ GETTEXT_LIBS = @LIBINTL@ OPENSSL_LIBS_NONSHARED = @OPENSSL_LIBS_NONSHARED@ GNUTLS_LIBS_NONSHARED = @GNUTLS_LIBS_NONSHARED@ +JANSSON_CPPFLAGS = @JANSSON_INC@ COMPRESS_CPPFLAGS += @ZLIB_INC@ @LZO_INC@ @FASTLZ_INC@ first_rule: all @@ -56,6 +57,10 @@ INCLUDES += -I$(srcdir) -I$(basedir) -I$(basedir)/include -I$(basedir)/lmdb @echo "Compiling $<" $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< #------------------------------------------------------------------------- +filed_conf.o: filed_conf.c + @echo "Compiling $<" + $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + compression.o: compression.c @echo "Compiling $<" $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(COMPRESS_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< @@ -64,24 +69,6 @@ all: Makefile bareos-fd @STATIC_FD@ @echo "==== Make of filed is good ====" @echo " " -win32/winlib.a: - @if test -f win32/Makefile -a "${GMAKE}" != "none"; then \ - (cd win32; $(GMAKE) DESTDIR=$(DESTDIR)); \ - fi - @rm -f bareos-fd.exe - -win32/winmain.o: - @if test -f win32/Makefile -a "${GMAKE}" != "none"; then \ - (cd win32; $(GMAKE) DESTDIR=$(DESTDIR)); \ - fi - @rm -f bareos-fd.exe - -win32/winres.res: - @if test -f win32/Makefile -a "${GMAKE}" != "none"; then \ - (cd win32; $(GMAKE) DESTDIR=$(DESTDIR)); \ - fi - @rm -f bareos-fd.exe - bareos-fd: Makefile $(SVROBJS) \ ../findlib/libbareosfind$(DEFAULT_ARCHIVE_TYPE) \ ../lib/libbareoscfg$(DEFAULT_ARCHIVE_TYPE) \ diff --git a/src/filed/filed.c b/src/filed/filed.c index 2722eadb516..fbf0a6be6af 100644 --- a/src/filed/filed.c +++ b/src/filed/filed.c @@ -224,11 +224,12 @@ int main (int argc, char *argv[]) } if (export_config_schema) { + POOL_MEM buffer; + my_config = new_config_parser(); init_fd_config(my_config, configfile, M_ERROR_TERM); - POOL_MEM buffer; print_config_schema_json(buffer); - printf( "%s\n", buffer.c_str() ); + printf("%s\n", buffer.c_str()); goto bail_out; } diff --git a/src/filed/filed_conf.c b/src/filed/filed_conf.c index 58faf886ccc..b76eed69d9b 100644 --- a/src/filed/filed_conf.c +++ b/src/filed/filed_conf.c @@ -41,6 +41,7 @@ * Kern Sibbald, September MM */ +#define NEED_JANSSON_NAMESPACE 1 #include "bareos.h" #include "filed.h" @@ -598,31 +599,40 @@ bool parse_fd_config(CONFIG *config, const char *configfile, int exit_code) /* * Print configuration file schema in json format */ +#ifdef HAVE_JANSSON bool print_config_schema_json(POOL_MEM &buffer) { RES_TABLE *resources = my_config->m_resources; - add_json_object_start(buffer, 0, ""); + initialize_json(); - add_json_pair(buffer, 1, "format-version", 2); - add_json_pair(buffer, 1, "component", "bareos-fd"); - add_json_pair(buffer, 1, "version", VERSION); + json_t *json = json_object(); + json_object_set_new(json, "format-version", json_integer(2)); + json_object_set_new(json, "component", json_string("bareos-fd")); + json_object_set_new(json, "version", json_string(VERSION)); /* * Resources */ - add_json_object_start(buffer, 1, "resource"); - add_json_object_start(buffer, 2, "bareos-fd"); + json_t *resource = json_object(); + json_object_set(json, "resource", resource); + json_t *bareos_fd = json_object(); + json_object_set(resource, "bareos-fd", bareos_fd); for (int r = 0; resources[r].name; r++) { RES_TABLE resource = my_config->m_resources[r]; - print_items_schema_json(buffer, 3, resource.name, resource.items, !resources[r + 1].name); + json_object_set(bareos_fd, resource.name, json_items(resource.items)); } - add_json_object_end(buffer, 2, "bareos-fd", true); - add_json_object_end(buffer, 1, "resource", true); - - add_json_object_end(buffer, 0, "", true); + pm_strcat(buffer, json_dumps(json, JSON_INDENT(2))); + json_decref(json); return true; } +#else +bool print_config_schema_json(POOL_MEM &buffer) +{ + pm_strcat(buffer, "{ \"success\": false, \"message\": \"not available\" }"); + return false; +} +#endif diff --git a/src/include/baconfig.h b/src/include/baconfig.h index 80001ea5aa7..67e0f734a53 100644 --- a/src/include/baconfig.h +++ b/src/include/baconfig.h @@ -294,6 +294,12 @@ typedef int (INTHANDLER)(); #define MODE_RW 0666 #endif +enum { + API_MODE_OFF = 0, + API_MODE_ON = 1, + API_MODE_JSON = 2 +}; + #if defined(HAVE_WIN32) typedef int64_t boffset_t; #define caddr_t char * diff --git a/src/lib/Makefile.in b/src/lib/Makefile.in index 32997a7bf84..083556b7082 100644 --- a/src/lib/Makefile.in +++ b/src/lib/Makefile.in @@ -20,6 +20,7 @@ thisdir = src/lib CPPFLAGS += @ZLIB_INC@ COMPRESS_CPPFLAGS += @ZLIB_INC@ @LZO_INC@ @FASTLZ_INC@ +JANSSON_CPPFLAGS = @JANSSON_INC@ DEBUG = @DEBUG@ CAM_LIBS = @CAM_LIBS@ @@ -27,6 +28,7 @@ CAP_LIBS = @CAP_LIBS@ ZLIB_LIBS = @ZLIB_LIBS@ LZO_LIBS = @LZO_LIBS@ FASTLZ_LIBS = @FASTLZ_LIBS@ +JANSSON_LIBS = @JANSSON_LIBS@ first_rule: all dummy: @@ -58,12 +60,13 @@ LIBBAREOS_SRCS = address_conf.c alist.c attr.c attribs.c base64.c \ cbuf.c compression.c cram-md5.c crypto.c crypto_cache.c \ crypto_gnutls.c crypto_none.c crypto_nss.c crypto_openssl.c \ crypto_wrap.c daemon.c devlock.c dlist.c edit.c fnmatch.c \ - guid_to_name.c hmac.c htable.c jcr.c lockmgr.c md5.c \ - mem_pool.c message.c mntent_cache.c passphrase.c path_list.c \ - plugins.c poll.c priv.c queue.c rblist.c runscript.c rwlock.c \ - scan.c scsi_crypto.c scsi_lli.c scsi_tapealert.c sellist.c \ - serial.c sha1.c signal.c smartall.c tls_gnutls.c tls_none.c \ - tls_nss.c tls_openssl.c tree.c util.c var.c watchdog.c workq.c + guid_to_name.c hmac.c htable.c jcr.c json.c lockmgr.c md5.c \ + mem_pool.c message.c mntent_cache.c output_formatter.c \ + passphrase.c path_list.c plugins.c poll.c priv.c queue.c \ + rblist.c runscript.c rwlock.c scan.c scsi_crypto.c scsi_lli.c \ + scsi_tapealert.c sellist.c serial.c sha1.c signal.c smartall.c \ + tls_gnutls.c tls_none.c tls_nss.c tls_openssl.c tree.c util.c \ + var.c watchdog.c workq.c LIBBAREOS_OBJS = $(LIBBAREOS_SRCS:.c=.o) LIBBAREOS_LOBJS = $(LIBBAREOS_SRCS:.c=.lo) @@ -155,6 +158,31 @@ compression.lo: compression.c @echo "Compiling $<" $(NO_ECHO)$(LIBTOOL_COMPILE) $(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(COMPRESS_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< +json.o: json.c + @echo "Compiling $<" + $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + +json.lo: json.c + @echo "Compiling $<" + $(NO_ECHO)$(LIBTOOL_COMPILE) $(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + +output_formatter.o: output_formatter.c + @echo "Compiling $<" + $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + +output_formatter.lo: output_formatter.c + @echo "Compiling $<" + $(NO_ECHO)$(LIBTOOL_COMPILE) $(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + +res.o: res.c + @echo "Compiling $<" + $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + +res.lo: res.c + @echo "Compiling $<" + $(NO_ECHO)$(LIBTOOL_COMPILE) $(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + + libbareos.a: $(LIBBAREOS_OBJS) @echo "Making $@ ..." $(AR) rc $@ $(LIBBAREOS_OBJS) @@ -163,7 +191,7 @@ libbareos.a: $(LIBBAREOS_OBJS) libbareos.la: Makefile $(LIBBAREOS_LOBJS) @echo "Making $@ ..." $(LIBTOOL_LINK) $(CXX) $(DEFS) $(DEBUG) $(LDFLAGS) -o $@ $(LIBBAREOS_LOBJS) -export-dynamic -rpath $(libdir) -release $(LIBBAREOS_LT_RELEASE) \ - $(WRAPLIBS) $(CAM_LIBS) $(CAP_LIBS) $(ZLIB_LIBS) $(LZO_LIBS) $(FASTLZ_LIBS) $(OPENSSL_LIBS) $(GNUTLS_LIBS) $(LIBS) $(DLLIBS) + $(WRAPLIBS) $(CAM_LIBS) $(CAP_LIBS) $(ZLIB_LIBS) $(LZO_LIBS) $(FASTLZ_LIBS) $(JANSSON_LIBS) $(OPENSSL_LIBS) $(GNUTLS_LIBS) $(LIBS) $(DLLIBS) libbareoscfg.a: $(LIBBAREOSCFG_OBJS) @echo "Making $@ ..." diff --git a/src/lib/json.c b/src/lib/json.c new file mode 100644 index 00000000000..3eb4264ab7f --- /dev/null +++ b/src/lib/json.c @@ -0,0 +1,59 @@ +/* + BAREOS® - Backup Archiving REcovery Open Sourced + + Copyright (C) 2015-2015 Bareos GmbH & Co. KG + + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +/* + * JSON Glue layer. + * + * This file is the glue between JANSSON and BAREOS. + * + * Joerg Steffens, April 2015 + */ + +#define NEED_JANSSON_NAMESPACE 1 +#include "bareos.h" + +#if HAVE_JANSSON +static pthread_once_t json_setup = PTHREAD_ONCE_INIT; + +static void *json_malloc(size_t size) +{ + return bmalloc(size); +} + +static void json_free(void *ptr) +{ + bfree(ptr); +} + +static void set_alloc_funcs() +{ + json_set_alloc_funcs(json_malloc, json_free); +} + +void initialize_json() +{ + pthread_once(&json_setup, set_alloc_funcs); +} +#else +void initialize_json() +{ +} +#endif /* HAVE_JANSSON */ diff --git a/src/lib/lib.h b/src/lib/lib.h index 0f346418c75..23cc2e4d604 100644 --- a/src/lib/lib.h +++ b/src/lib/lib.h @@ -41,6 +41,7 @@ #include "serial.h" #include "message.h" #include "lex.h" +#include "output_formatter.h" #include "parse_conf.h" #include "tls.h" #include "address_conf.h" diff --git a/src/lib/mem_pool.c b/src/lib/mem_pool.c index d97205a2526..dae8121861b 100644 --- a/src/lib/mem_pool.c +++ b/src/lib/mem_pool.c @@ -683,27 +683,83 @@ void POOL_MEM::realloc_pm(int32_t size) V(mutex); } +int POOL_MEM::strcat(POOL_MEM &str) +{ + return strcat(str.c_str()); +} + int POOL_MEM::strcat(const char *str) { - int pmlen = strlen(mem); + int pmlen = strlen(); int len; if (!str) str = ""; - len = strlen(str) + 1; + len = ::strlen(str) + 1; check_size(pmlen + len); memcpy(mem+pmlen, str, len); return pmlen + len - 1; } +int POOL_MEM::strcpy(POOL_MEM &str) +{ + return strcpy(str.c_str()); +} + int POOL_MEM::strcpy(const char *str) { int len; if (!str) str = ""; - len = strlen(str) + 1; + len = ::strlen(str) + 1; check_size(len); memcpy(mem, str, len); return len - 1; } + +int POOL_MEM::bsprintf(const char *fmt, ...) +{ + int len; + va_list arg_ptr; + va_start(arg_ptr, fmt); + len = bvsprintf(fmt, arg_ptr); + va_end(arg_ptr); + return len; +} + +#ifdef HAVE_VA_COPY +int POOL_MEM::bvsprintf(const char *fmt, va_list arg_ptr) +{ + int maxlen, len; + va_list ap; + +again: + maxlen = max_size() - 1; + va_copy(ap, arg_ptr); + len = ::bvsnprintf(mem, maxlen, fmt, ap); + va_end(ap); + if (len < 0 || len >= maxlen) { + realloc_pm(maxlen + maxlen / 2); + goto again; + } + return len; +} + +#else /* no va_copy() -- brain damaged version of variable arguments */ + +int POOL_MEM::bvsprintf(const char *fmt, va_list arg_ptr) +{ + int maxlen, len; + + realloc_pm(5000); + maxlen = max_size() - 1; + len = ::bvsnprintf(mem, maxlen, fmt, arg_ptr); + if (len < 0 || len >= maxlen) { + if (len >= maxlen) { + len = -len; + } + } + return len; +} +#endif diff --git a/src/lib/mem_pool.h b/src/lib/mem_pool.h index 642402e97c5..1de97579240 100644 --- a/src/lib/mem_pool.h +++ b/src/lib/mem_pool.h @@ -88,6 +88,7 @@ class POOL_MEM { public: POOL_MEM() { mem = get_pool_memory(PM_NAME); *mem = 0; } POOL_MEM(int pool) { mem = get_pool_memory(pool); *mem = 0; } + POOL_MEM(const char *str) { mem = get_pool_memory(PM_NAME); *mem = 0; strcpy(str); } ~POOL_MEM() { free_pool_memory(mem); mem = NULL; } char *c_str() const { return mem; } POOLMEM *&addr() { return mem; } @@ -98,8 +99,13 @@ class POOL_MEM { } int32_t max_size(); void realloc_pm(int32_t size); + int strcpy(POOL_MEM &str); int strcpy(const char *str); + int strcat(POOL_MEM &str); int strcat(const char *str); + size_t strlen() { return ::strlen(mem); }; + int bsprintf(const char *fmt, ...); + int bvsprintf(const char *fmt, va_list arg_ptr); }; int pm_strcat(POOLMEM **pm, const char *str); diff --git a/src/lib/output_formatter.c b/src/lib/output_formatter.c new file mode 100644 index 00000000000..8f435a55ac4 --- /dev/null +++ b/src/lib/output_formatter.c @@ -0,0 +1,291 @@ +/* + BAREOS® - Backup Archiving REcovery Open Sourced + + Copyright (C) 2015-2015 Bareos GmbH & Co. KG + + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +/* + * Output Formatter routines. + * + * The idea behind these routines is that output to the user (user inferfaces, UIs) + * is handled centrally with this class. + * + * Joerg Steffens, April 2015 + */ + +#define NEED_JANSSON_NAMESPACE 1 +#include "bareos.h" + +OUTPUT_FORMATTER::OUTPUT_FORMATTER(SEND_HANDLER *send_func_arg, void *send_ctx_arg, int api_mode) +{ + initialize_json(); + + send_func = send_func_arg; + send_ctx = send_ctx_arg; + api = api_mode; + + result_message_plain = new POOL_MEM(PM_MESSAGE); +#if HAVE_JANSSON + result_array_json = json_array(); + result_stack_json = New(alist(10, false)); +#endif +} + +OUTPUT_FORMATTER::~OUTPUT_FORMATTER() +{ + delete result_message_plain; +#if HAVE_JANSSON + json_object_clear(result_array_json); + json_decref(result_array_json); + delete result_stack_json; +#endif +} + +void OUTPUT_FORMATTER::object_start(const char *name) +{ +#if HAVE_JANSSON + json_t *json_object_current = NULL; + json_t *json_object_existing = NULL; + json_t *json_object_array = NULL; + json_t *json_object_new = json_object(); +#endif + + Dmsg1(800, "obj start: %s\n", name); + switch (api) { +#if HAVE_JANSSON + case API_MODE_JSON: + json_object_current = (json_t *)result_stack_json->last(); + if (json_object_current == NULL) { + json_object_current = json_object(); + json_array_append_new(result_array_json, json_object_current); + } + if (name == NULL) { + /* + * Add nameless object. + */ + result_stack_json->push(json_object_current); + } else { + json_object_existing = json_object_get(json_object_current, name); + if (json_object_existing) { + if (json_is_array(json_object_existing)) { + Dmsg2(800, "json object %s (stack size: %d) already exits and is an array: appending\n", + name, result_stack_json->size()); + json_array_append_new(json_object_existing, json_object_new); + } else { + Dmsg2(800, "json object %s (stack size: %d) already exits: converting to array and appending\n", + name, result_stack_json->size()); + json_object_array = json_array(); + json_array_append_new(json_object_array, json_object_existing); + json_array_append_new(json_object_array, json_object_new); + json_object_set(json_object_current, name, json_object_array); + } + } else { + Dmsg2(800, "create new json object %s (stack size: %d)\n", + name, result_stack_json->size()); + json_object_set(json_object_current, name, json_object_new); + } + result_stack_json->push(json_object_new); + } + Dmsg1(800, "result stack: %d\n", result_stack_json->size()); + break; +#endif + default: + break; + } +} + +void OUTPUT_FORMATTER::object_end(const char *name) +{ + Dmsg1(800, "obj end: %s\n", name); + switch (api) { +#if HAVE_JANSSON + case API_MODE_JSON: + result_stack_json->pop(); + Dmsg1(800, "result stack: %d\n", result_stack_json->size()); + break; +#endif + default: + process_text_buffer(); + break; + } +} + +void OUTPUT_FORMATTER::decoration(const char *fmt, ...) +{ + POOL_MEM string; + va_list arg_ptr; + + switch (api) { + case API_MODE_ON: + case API_MODE_JSON: + break; + default: + va_start(arg_ptr, fmt); + string.bvsprintf(fmt, arg_ptr); + result_message_plain->strcat(string); + va_end(arg_ptr); + break; + } +} + +void OUTPUT_FORMATTER::object_key_value(const char *key, uint64_t value) +{ + object_key_value(key, NULL, value, NULL); +} + +void OUTPUT_FORMATTER::object_key_value(const char *key, uint64_t value, const char *value_fmt) +{ + object_key_value(key, NULL, value, value_fmt); +} + +void OUTPUT_FORMATTER::object_key_value(const char *key, const char *key_fmt, uint64_t value, const char *value_fmt) +{ + POOL_MEM string; + + switch (api) { +#if HAVE_JANSSON + case API_MODE_JSON: + json_key_value_add(key, value); + break; +#endif + default: + if (key_fmt) { + string.bsprintf(key_fmt, key); + result_message_plain->strcat(string); + } + if (value_fmt) { + string.bsprintf(value_fmt, value); + result_message_plain->strcat(string); + } + } +} + +void OUTPUT_FORMATTER::object_key_value(const char *key, const char *value) +{ + object_key_value(key, NULL, value, NULL); +} + +void OUTPUT_FORMATTER::object_key_value(const char *key, const char *value, const char *value_fmt) +{ + object_key_value(key, NULL, value, value_fmt); +} + +void OUTPUT_FORMATTER::object_key_value(const char *key, const char *key_fmt, const char *value, const char *value_fmt) +{ + POOL_MEM string; + + switch (api) { +#if HAVE_JANSSON + case API_MODE_JSON: + json_key_value_add(key, value); + break; +#endif + default: + if (key_fmt) { + string.bsprintf(key_fmt, key); + result_message_plain->strcat(string); + } + if (value_fmt) { + string.bsprintf(value_fmt, value); + result_message_plain->strcat(string); + } + Dmsg2(800, "obj: %s:%s\n", key, value); + } +} + +void OUTPUT_FORMATTER::process_text_buffer() +{ + if (result_message_plain->strlen() > 0) { + send_func(send_ctx, result_message_plain->c_str()); + result_message_plain->strcpy(""); + } +} + +void OUTPUT_FORMATTER::finalize_result(bool result) +{ + switch (api) { +#if HAVE_JANSSON + case API_MODE_JSON: + json_finalize_result(result); + break; +#endif + default: + process_text_buffer(); + break; + } +} + +#if HAVE_JANSSON +void OUTPUT_FORMATTER::json_key_value_add(const char *key, uint64_t value) +{ + json_t *json_obj = NULL; + + json_obj = (json_t *)result_stack_json->last(); + if (json_obj != NULL) { + json_object_set(json_obj, key, json_integer(value)); + } else { + Emsg2(M_ERROR, 0, "no json object defined to add %s: %llu", key, value); + } +} + + +void OUTPUT_FORMATTER::json_key_value_add(const char *key, const char *value) +{ + json_t *json_obj = NULL; + + json_obj = (json_t *)result_stack_json->last(); + if (json_obj != NULL) { + json_object_set(json_obj, key, json_string(value)); + } else { + Emsg2(M_ERROR, 0, "no json object defined to add %s: %s", key, value); + } +} + +void OUTPUT_FORMATTER::json_add_result(json_t *json) +{ + json_array_append_new(result_array_json, json); +} + +void OUTPUT_FORMATTER::json_finalize_result(bool result) +{ + POOL_MEM string; + json_t *msg_obj = json_object(); + json_t *error_obj; + + /* + * We mimic json-rpc result and error messages, + * To make it easier to implement real json-rpc later on. + */ + json_object_set(msg_obj, "jsonrpc", json_string("2.0")); + json_object_set(msg_obj, "id", json_null()); + if (result) { + json_object_set(msg_obj, "result", result_array_json); + } else { + error_obj=json_object(); + json_object_set_new(error_obj, "code", json_integer(1)); + json_object_set_new(error_obj, "message", json_string("failed")); + json_object_set(error_obj, "data", result_array_json); + json_object_set_new(msg_obj, "error", error_obj); + } + + string.bsprintf("%s\n", json_dumps(msg_obj, UA_JSON_FLAGS)); + send_func(send_ctx, string.c_str()); + json_array_clear(result_array_json); + json_object_clear(msg_obj); +} +#endif diff --git a/src/lib/output_formatter.h b/src/lib/output_formatter.h new file mode 100644 index 00000000000..5c474fc8cc6 --- /dev/null +++ b/src/lib/output_formatter.h @@ -0,0 +1,88 @@ +/* + BAREOS® - Backup Archiving REcovery Open Sourced + + Copyright (C) 2015-2015 Bareos GmbH & Co. KG + + This program is Free Software; you can redistribute it and/or + modify it under the terms of version three of the GNU Affero General Public + License as published by the Free Software Foundation and included + in the file LICENSE. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +/* + * Output Formatter prototypes + * + * Joerg Steffens, April 2015 + */ + +#ifndef __OUTPUT_FORMATTER_H_ +#define __OUTPUT_FORMATTER_H_ + +#if HAVE_JANSSON +#define UA_JSON_FLAGS JSON_INDENT(2) + +/* + * See if the source file needs the full JANSSON namespace or that we can + * get away with using a forward declaration of the json_t struct. + */ +#ifndef NEED_JANSSON_NAMESPACE +typedef struct json_t json_t; +#else +#include +#endif +#endif + +class OUTPUT_FORMATTER : public SMARTALLOC { +public: + typedef void (SEND_HANDLER)(void *, const char *); + + OUTPUT_FORMATTER(SEND_HANDLER *send_func, void *send_ctx, int api_mode = API_MODE_OFF); + ~OUTPUT_FORMATTER(); + + void set_mode(int mode) { api = mode; }; + int get_mode() { return api; }; + + void object_start(const char *name = NULL); + void object_end(const char *name = NULL); + void decoration(const char *fmt, ...); + void object_key_value(const char *key, uint64_t value); + void object_key_value(const char *key, uint64_t value, const char *value_fmt); + void object_key_value(const char *key, const char *key_fmt, uint64_t value, const char *value_fmt); + void object_key_value(const char *key, const char *value); + void object_key_value(const char *key, const char *value, const char *value_fmt); + void object_key_value(const char *key, const char *key_fmt, const char *value, const char *value_fmt); + + void finalize_result(bool result); + +#if HAVE_JANSSON + void json_add_result(json_t *json); + void json_key_value_add(const char *key, uint64_t value); + void json_key_value_add(const char *key, const char *value); + void json_finalize_result(bool result); +#endif + +private: + int api; + SEND_HANDLER *send_func; + void *send_ctx; + POOL_MEM *result_message_plain; + + void process_text_buffer(); + +#if HAVE_JANSSON + json_t *result_array_json; + alist *result_stack_json; +#endif +}; + +#endif diff --git a/src/lib/parse_conf.h b/src/lib/parse_conf.h index 85e31ab27fd..7543e734acf 100644 --- a/src/lib/parse_conf.h +++ b/src/lib/parse_conf.h @@ -387,23 +387,14 @@ void save_resource(int type, RES_ITEM *item, int pass); bool store_resource(int type, LEX *lc, RES_ITEM *item, int index, int pass); const char *res_to_str(int rcode); -/* - * JSON print functions. - */ -bool print_items_schema_json(POOL_MEM &buff, int level, const char *name, RES_ITEM items[], const bool last = false); -bool print_items_schema_json(POOL_MEM &buff, int level, const char *name, s_kw items[], const bool last = false); - -bool print_item_schema_json(POOL_MEM &buff, int level, RES_ITEM *item, const bool last = false); -bool print_item_schema_json(POOL_MEM &buff, int level, s_kw *item, const bool last = false); - +#ifdef HAVE_JANSSON /* * JSON output helper functions */ -void add_json_object_start(POOL_MEM &cfg_str, int level, const char *string); -void add_json_object_end(POOL_MEM &cfg_str, int level, const char *string, const bool last = false); -void add_json_pair_plain(POOL_MEM &cfg_str, int level, const char *string, const char *value, const bool last = false); -void add_json_pair(POOL_MEM &cfg_str, int level, const char *string, const char *value, const bool last = false); -void add_json_pair(POOL_MEM &cfg_str, int level, const char *string, int value, const bool last = false); +json_t *json_item(s_kw *item); +json_t *json_item(RES_ITEM *item); +json_t *json_items(RES_ITEM items[]); +#endif /* * Loop through each resource of type, returning in var diff --git a/src/lib/protos.h b/src/lib/protos.h index ba160a4d974..e70b7623667 100644 --- a/src/lib/protos.h +++ b/src/lib/protos.h @@ -242,6 +242,9 @@ void remove_jcr_from_tsd(JCR *jcr); uint32_t get_jobid_from_tsd(); uint32_t get_jobid_from_tid(pthread_t tid); +/* json.c */ +void initialize_json(); + /* lex.c */ LEX *lex_close_file(LEX *lf); LEX *lex_open_file(LEX *lf, diff --git a/src/lib/res.c b/src/lib/res.c index d31812935da..cf548ab2381 100644 --- a/src/lib/res.c +++ b/src/lib/res.c @@ -26,6 +26,7 @@ * Split from parse_conf.c April MMV */ +#define NEED_JANSSON_NAMESPACE 1 #include "bareos.h" #include "generic_res.h" @@ -1828,68 +1829,9 @@ bool BRSRES::print_config(POOL_MEM &buff, bool hide_sensitive_data) return true; } -static void add_indent(POOL_MEM &cfg_str, int level) -{ - for (int i = 0; i < level; i++) { - pm_strcat(cfg_str, DEFAULT_INDENT_STRING); - } -} - -void add_json_pair_plain(POOL_MEM &cfg_str, int level, const char *string, const char *value, const bool last) -{ - POOL_MEM temp; - - add_indent(cfg_str, level); - Mmsg(temp, "\"%s\": %s", string, value); - pm_strcat(cfg_str, temp.c_str()); - if ( last ) { - pm_strcat(cfg_str, "\n"); - } else { - pm_strcat(cfg_str, ",\n"); - } -} - -void add_json_pair(POOL_MEM &cfg_str, int level, const char *string, const char *value, const bool last) -{ - POOL_MEM temp; - - Mmsg(temp, "\"%s\"", value); - add_json_pair_plain(cfg_str, level, string, temp.c_str(), last); -} - -void add_json_pair(POOL_MEM &cfg_str, int level, const char *string, int value, const bool last) -{ - POOL_MEM temp; - - Mmsg(temp, "%d", value); - add_json_pair_plain(cfg_str, level, string, temp.c_str(), last); -} - -void add_json_object_start(POOL_MEM &cfg_str, int level, const char *string) -{ - POOL_MEM temp; - - add_indent(cfg_str, level); - if (bstrcmp(string, "")) { - Mmsg(temp, "{\n"); - } else { - Mmsg(temp, "\"%s\": {\n", string); - } - pm_strcat(cfg_str, temp.c_str()); -} - -void add_json_object_end(POOL_MEM &cfg_str, int level, const char *string, const bool last) -{ - add_indent(cfg_str, level); - if (last) { - pm_strcat(cfg_str, "}\n"); - } else { - pm_strcat(cfg_str, "},\n"); - } -} - +#ifdef HAVE_JANSSON /* - * prints a resource item schema description in JSON format. + * resource item schema description in JSON format. * Example output: * * "filesetacl": { @@ -1905,67 +1847,66 @@ void add_json_object_end(POOL_MEM &cfg_str, int level, const char *string, const * "type": "RES_ITEM" * } */ -bool print_item_schema_json(POOL_MEM &buff, int level, RES_ITEM *item, const bool last) +json_t *json_item(RES_ITEM *item) { - add_json_object_start(buff, level, item->name); + json_t *json = json_object(); - add_json_pair(buff, level + 1, "datatype", datatype_to_str(item->type)); + json_object_set_new(json, "datatype", json_string(datatype_to_str(item->type))); + json_object_set_new(json, "code", json_integer(item->code)); if (item->flags & CFG_ITEM_ALIAS) { - add_json_pair(buff, level + 1, "alias", "true"); + json_object_set_new(json, "alias", json_true()); } if (item->flags & CFG_ITEM_DEFAULT) { - add_json_pair(buff, level + 1, "default_value", item->default_value); + /* FIXME? would it be better to convert it to the right type before returning? */ + json_object_set_new(json, "default_value", json_string(item->default_value)); } if (item->flags & CFG_ITEM_PLATFORM_SPECIFIC) { - add_json_pair(buff, level + 1, "platform_specific", "true"); + json_object_set_new(json, "platform_specific", json_true()); } if (item->flags & CFG_ITEM_DEPRECATED) { - add_json_pair_plain(buff, level + 1, "deprecated", "true"); + json_object_set_new(json, "deprecated", json_true()); } if (item->flags & CFG_ITEM_NO_EQUALS) { - add_json_pair_plain(buff, level + 1, "equals", "false"); + json_object_set_new(json, "equals", json_false()); } else { - add_json_pair_plain(buff, level + 1, "equals", "true"); + json_object_set_new(json, "equals", json_true()); } if (item->flags & CFG_ITEM_REQUIRED) { - add_json_pair_plain(buff, level + 1, "required", "true"); + json_object_set_new(json, "required", json_true()); } if (item->versions) { - add_json_pair(buff, level + 1, "versions", item->versions); + json_object_set_new(json, "versions", json_string(item->versions)); } if (item->description) { - add_json_pair(buff, level + 1, "description", item->description); + json_object_set_new(json, "description", json_string(item->description)); } - add_json_pair(buff, level + 1, "code", item->code, true); - - add_json_object_end(buff, level, item->name, last); - - return true; + return json; } -bool print_item_schema_json(POOL_MEM &buff, int level, s_kw *item, const bool last) +json_t *json_item(s_kw *item) { - add_json_object_start(buff, level, item->name); - add_json_pair(buff, level + 1, "token", item->token, true); - add_json_object_end(buff, level, item->name, last); + json_t *json = json_object(); - return true; + json_object_set_new(json, "token", json_integer(item->token)); + + return json; } -bool print_items_schema_json(POOL_MEM &buffer, int level, const char *name, RES_ITEM items[], const bool last) +json_t *json_items(RES_ITEM items[]) { - add_json_object_start(buffer, level, name); + json_t *json = json_object(); + if (items) { for (int i = 0; items[i].name; i++) { - print_item_schema_json(buffer, level+1, &items[i], items[i+1].name == NULL); + json_object_set_new(json, items[i].name, json_item(&items[i])); } } - add_json_object_end(buffer, level, name, last); - return true; + return json; } +#endif static DATATYPE_NAME datatype_names[] = { /* diff --git a/src/qt-tray-monitor/tray-monitor.cpp b/src/qt-tray-monitor/tray-monitor.cpp index 9bf7e9ad3b5..2f2f607a2df 100644 --- a/src/qt-tray-monitor/tray-monitor.cpp +++ b/src/qt-tray-monitor/tray-monitor.cpp @@ -211,11 +211,12 @@ int main(int argc, char *argv[]) parse_command_line(argc, argv, cl); if (cl.export_config_schema) { + POOL_MEM buffer; + my_config = new_config_parser(); init_tmon_config(my_config, cl.configfile, M_ERROR_TERM); - POOL_MEM buffer; print_config_schema_json(buffer); - printf( "%s\n", buffer.c_str() ); + printf("%s\n", buffer.c_str()); fflush(stdout); exit(0); } diff --git a/src/qt-tray-monitor/tray-monitor.pro.in b/src/qt-tray-monitor/tray-monitor.pro.in index 67436ee22ba..3ad7dcaec10 100644 --- a/src/qt-tray-monitor/tray-monitor.pro.in +++ b/src/qt-tray-monitor/tray-monitor.pro.in @@ -16,6 +16,7 @@ CONFIG( debug, debug|release ) { } QMAKE_LIBDIR += ../lib +QMAKE_CXXFLAGS += @JANSSON_INC@ LIBS += -lbareoscfg -lbareos bins.path = /$(DESTDIR)@bindir@ diff --git a/src/qt-tray-monitor/tray_conf.cpp b/src/qt-tray-monitor/tray_conf.cpp index 93f5ebd8ae5..71f9b832557 100644 --- a/src/qt-tray-monitor/tray_conf.cpp +++ b/src/qt-tray-monitor/tray_conf.cpp @@ -40,6 +40,7 @@ * Nicolas Boichat, August MMIV */ +#define NEED_JANSSON_NAMESPACE 1 #include "bareos.h" #include "tray_conf.h" @@ -384,31 +385,40 @@ bool parse_tmon_config(CONFIG *config, const char *configfile, int exit_code) /* * Print configuration file schema in json format */ +#ifdef HAVE_JANSSON bool print_config_schema_json(POOL_MEM &buffer) { RES_TABLE *resources = my_config->m_resources; - add_json_object_start(buffer, 0, ""); + initialize_json(); - add_json_pair(buffer, 1, "format-version", 2); - add_json_pair(buffer, 1, "component", "bareos-tray-monitor"); - add_json_pair(buffer, 1, "version", VERSION); + json_t *json = json_object(); + json_object_set_new(json, "format-version", json_integer(2)); + json_object_set_new(json, "component", json_string("bareos-tray-monitor")); + json_object_set_new(json, "version", json_string(VERSION)); /* * Resources */ - add_json_object_start(buffer, 1, "resource"); - add_json_object_start(buffer, 2, "bareos-tray-monitor"); + json_t *resource = json_object(); + json_object_set(json, "resource", resource); + json_t *bareos_tray_monitor = json_object(); + json_object_set(resource, "bareos-tray-monitor", bareos_tray_monitor); for (int r = 0; resources[r].name; r++) { RES_TABLE resource = my_config->m_resources[r]; - print_items_schema_json(buffer, 3, resource.name, resource.items, !resources[r + 1].name); + json_object_set(bareos_tray_monitor, resource.name, json_items(resource.items)); } - add_json_object_end(buffer, 2, "bareos-tray-monitor", true); - add_json_object_end(buffer, 1, "resource", true); - - add_json_object_end(buffer, 0, "", true); + pm_strcat(buffer, json_dumps(json, JSON_INDENT(2))); + json_decref(json); return true; } +#else +bool print_config_schema_json(POOL_MEM &buffer) +{ + pm_strcat(buffer, "{ \"success\": false, \"message\": \"not available\" }"); + return false; +} +#endif diff --git a/src/stored/Makefile.in b/src/stored/Makefile.in index 9f97bc96820..6f6bf97b891 100644 --- a/src/stored/Makefile.in +++ b/src/stored/Makefile.in @@ -92,6 +92,8 @@ GLUSTER_INC = @GLUSTER_INC@ RADOS_INC = @RADOS_INC@ INCLUDES += -I$(srcdir) -I$(basedir) -I$(basedir)/include +JANSSON_CPPFLAGS = @JANSSON_INC@ + DEBUG = @DEBUG@ GETTEXT_LIBS = @LIBINTL@ DB_LIBS = @DB_LIBS@ @@ -151,6 +153,14 @@ rados_device.lo: rados_device.c @echo "Compiling $<" $(NO_ECHO)$(LIBTOOL_COMPILE) $(CXX) $(DEFS) $(DEBUG) $(RADOS_INC) -c $(WCFLAGS) $(CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< +stored_conf.o: stored_conf.c + @echo "Compiling $<" + $(NO_ECHO)$(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + +stored_conf.lo: stored_conf.c + @echo "Compiling $<" + $(NO_ECHO)$(LIBTOOL_COMPILE) $(CXX) $(DEFS) $(DEBUG) -c $(WCFLAGS) $(CPPFLAGS) $(JANSSON_CPPFLAGS) $(INCLUDES) $(DINCLUDE) $(CXXFLAGS) $< + bareos-sd: Makefile libbareossd$(DEFAULT_ARCHIVE_TYPE) $(SDOBJS) \ ../lib/libbareoscfg$(DEFAULT_ARCHIVE_TYPE) \ ../lib/libbareos$(DEFAULT_ARCHIVE_TYPE) \ diff --git a/src/stored/stored.c b/src/stored/stored.c index 72dc271c3a3..45b4b6e0f0c 100644 --- a/src/stored/stored.c +++ b/src/stored/stored.c @@ -225,11 +225,12 @@ int main (int argc, char *argv[]) } if (export_config_schema) { + POOL_MEM buffer; + my_config = new_config_parser(); init_sd_config(my_config, configfile, M_ERROR_TERM); - POOL_MEM buffer; print_config_schema_json(buffer); - printf( "%s\n", buffer.c_str() ); + printf("%s\n", buffer.c_str()); goto bail_out; } diff --git a/src/stored/stored_conf.c b/src/stored/stored_conf.c index 2099aa425b3..8febcd50670 100644 --- a/src/stored/stored_conf.c +++ b/src/stored/stored_conf.c @@ -26,6 +26,7 @@ * Kern Sibbald, March MM */ +#define NEED_JANSSON_NAMESPACE 1 #include "bareos.h" #include "stored.h" @@ -391,7 +392,7 @@ static void store_maxblocksize(LEX *lc, RES_ITEM *item, int index, int pass) store_resource(CFG_TYPE_SIZE32, lc, item, index, pass); if (*(uint32_t *)(item->value) > MAX_BLOCK_LENGTH) { scan_err2(lc, _("Maximum Block Size configured value %u is greater than allowed maximum: %u"), - *(uint32_t *)(item->value), MAX_BLOCK_LENGTH ); + *(uint32_t *)(item->value), MAX_BLOCK_LENGTH); } } @@ -908,7 +909,7 @@ bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code) { bool retval; - init_sd_config( config, configfile, exit_code ); + init_sd_config(config, configfile, exit_code); retval = config->parse_config(); if (retval) { @@ -929,31 +930,40 @@ bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code) /* * Print configuration file schema in json format */ +#ifdef HAVE_JANSSON bool print_config_schema_json(POOL_MEM &buffer) { RES_TABLE *resources = my_config->m_resources; - add_json_object_start(buffer, 0, ""); + initialize_json(); - add_json_pair(buffer, 1, "format-version", 2); - add_json_pair(buffer, 1, "component", "bareos-sd"); - add_json_pair(buffer, 1, "version", VERSION); + json_t *json = json_object(); + json_object_set_new(json, "format-version", json_integer(2)); + json_object_set_new(json, "component", json_string("bareos-sd")); + json_object_set_new(json, "version", json_string(VERSION)); /* * Resources */ - add_json_object_start(buffer, 1, "resource"); - add_json_object_start(buffer, 2, "bareos-sd"); + json_t *resource = json_object(); + json_object_set(json, "resource", resource); + json_t *bareos_sd = json_object(); + json_object_set(resource, "bareos-sd", bareos_sd); for (int r = 0; resources[r].name; r++) { RES_TABLE resource = my_config->m_resources[r]; - print_items_schema_json(buffer, 3, resource.name, resource.items, !resources[r + 1].name); + json_object_set(bareos_sd, resource.name, json_items(resource.items)); } - add_json_object_end(buffer, 2, "bareos-sd", true); - add_json_object_end(buffer, 1, "resource", true); - - add_json_object_end(buffer, 0, "", true); + pm_strcat(buffer, json_dumps(json, JSON_INDENT(2))); + json_decref(json); return true; } +#else +bool print_config_schema_json(POOL_MEM &buffer) +{ + pm_strcat(buffer, "{ \"success\": false, \"message\": \"not available\" }"); + return false; +} +#endif diff --git a/src/win32/lib/Makefile b/src/win32/lib/Makefile index ac8c54095fd..49709c1e559 100644 --- a/src/win32/lib/Makefile +++ b/src/win32/lib/Makefile @@ -41,8 +41,9 @@ LIBBAREOS_SRCS = address_conf.c alist.c attr.c attribs.c base64.c \ compression.c cram-md5.c cbuf.c crypto.c crypto_cache.c \ crypto_gnutls.c crypto_none.c crypto_nss.c crypto_openssl.c \ crypto_wrap.c daemon.c devlock.c dlist.c edit.c fnmatch.c \ - guid_to_name.c hmac.c htable.c jcr.c lockmgr.c md5.c \ - mem_pool.c message.c mntent_cache.c passphrase.c path_list.c \ + guid_to_name.c hmac.c htable.c jcr.c json.c lockmgr.c md5.c \ + mem_pool.c message.c mntent_cache.c output_formatter.c \ + passphrase.c path_list.c \ plugins.c poll.c priv.c queue.c rblist.c runscript.c rwlock.c \ scan.c scsi_crypto.c scsi_lli.c sellist.c serial.c sha1.c \ signal.c smartall.c tls_gnutls.c tls_none.c tls_nss.c \