]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
Add bconsole .jlist command to get JSON output from regular list commands
authorEric Bollengier <eric@baculasystems.com>
Wed, 31 Mar 2021 15:38:43 +0000 (17:38 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 24 Mar 2022 08:03:00 +0000 (09:03 +0100)
bacula/src/cats/cats.h
bacula/src/cats/sql.c
bacula/src/cats/sql_list.c
bacula/src/dird/ua_dotcmds.c
bacula/src/dird/ua_output.c

index 9e8eefcbaa724cc8eaef0088b66902e0e26d7229..31fdf0b0d10e258d2cb2cc584ef99fc8f798c9a7 100644 (file)
@@ -665,6 +665,7 @@ enum e_list_type {
    HORZ_LIST,                   /* list */
    VERT_LIST,                   /* llist */
    ARG_LIST,                    /* key1=v1 key2=v2 key3=v3 */
+   JSON_LIST,
    FAILED_JOBS,
    INCOMPLETE_JOBS
 };
index 57e35ce5d27e04fbf57ba9f0b4fbe1c58ca6472b..e07985a25eb0996c160e6da380a163cafea1e061 100644 (file)
@@ -796,7 +796,10 @@ int list_result(void *vctx, int nb_col, char **row)
       if (type == ARG_LIST) { 
          goto arg_list; 
       } 
+      if (type == JSON_LIST) {
+         goto json_list;
+      }
+
       Dmsg1(800, "list_result starts second loop looking at %d fields\n",  
             mdb->sql_num_fields()); 
  
@@ -882,7 +885,33 @@ arg_list:
    } 
    send(ctx, "\n"); 
    return 0; 
+
+json_list:
+   POOL_MEM tmp, qval, qkey;
+   bool first=true;
+
+   Dmsg1(800, "list_result starts json list at %d fields\n", mdb->sql_num_fields());
+   mdb->sql_field_seek(0);
+
+   send(ctx, "{");
+   for (i = 0; i < mdb->sql_num_fields(); i++) {
+      field = mdb->sql_fetch_field();
+      if (!field) {
+         break;
+      }
+      quote_string(qkey.addr(), field->name);
+      lcase(qkey.c_str());
+      if (!mdb->sql_field_is_numeric(field->type)) {
+         quote_string(qval.addr(), NPRTB(row[i]));
+      } else {
+         pm_strcpy(qval, row[i]);
+      }
+      Mmsg(tmp, "%s%s: %s", first?"":",", qkey.c_str(), qval.c_str());
+      send(ctx, tmp.c_str());
+      first = false;
+   }
+   send(ctx, "}\n");
+   return 0;
 } 
  
 /* 
@@ -899,8 +928,12 @@ list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type ty
    char buf[2000], ewc[30]; 
  
    Dmsg0(800, "list_result starts\n"); 
-   if (mdb->sql_num_rows() == 0) { 
-      send(ctx, _("No results to list.\n")); 
+   if (mdb->sql_num_rows() == 0) {
+      if (type == JSON_LIST) {
+         send(ctx, "[]\n");
+      } else {
+         send(ctx, _("No results to list.\n"));
+      }
       return mdb->sql_num_rows(); 
    } 
  
@@ -939,7 +972,10 @@ list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type ty
    if (type == ARG_LIST) { 
       goto arg_list; 
    } 
+   if (type == JSON_LIST) {
+      goto json_list;
+   }
+
    Dmsg1(800, "list_result starts second loop looking at %d fields\n", mdb->sql_num_fields()); 
    list_dashes(mdb, send, ctx); 
    send(ctx, "|"); 
@@ -983,8 +1019,7 @@ list_result(JCR *jcr, BDB *mdb, DB_LIST_HANDLER *send, void *ctx, e_list_type ty
    list_dashes(mdb, send, ctx); 
    return mdb->sql_num_rows(); 
  
-vertical_list: 
+vertical_list:
    Dmsg1(800, "list_result starts vertical list at %d fields\n", mdb->sql_num_fields()); 
    while ((row = mdb->sql_fetch_row()) != NULL) { 
       mdb->sql_field_seek(0); 
@@ -1007,8 +1042,7 @@ vertical_list:
       send(ctx, "\n"); 
    } 
  
-arg_list: 
+arg_list:
    Dmsg1(800, "list_result starts arg list at %d fields\n", mdb->sql_num_fields()); 
    while ((row = mdb->sql_fetch_row()) != NULL) { 
       mdb->sql_field_seek(0); 
@@ -1027,7 +1061,39 @@ arg_list:
       } 
       send(ctx, "\n"); 
    } 
-   return mdb->sql_num_rows(); 
+   return mdb->sql_num_rows();
+
+json_list:
+   Dmsg1(800, "list_result starts json list at %d fields\n", mdb->sql_num_fields());
+   POOL_MEM tmp, qval, qkey;
+   bool rfirst=true;
+   send(ctx, "[");
+   while ((row = mdb->sql_fetch_row()) != NULL) {
+      bool first=true;
+      send(ctx, rfirst?"{" : ",{");
+      rfirst = false;
+
+      mdb->sql_field_seek(0);
+      for (i = 0; i < mdb->sql_num_fields(); i++) {
+         field = mdb->sql_fetch_field();
+         if (!field) {
+            break;
+         }
+         quote_string(qkey.addr(), field->name);
+         lcase(qkey.c_str());
+         if (!mdb->sql_field_is_numeric(field->type)) {
+            quote_string(qval.addr(), NPRTB(row[i]));
+         } else {
+            pm_strcpy(qval, row[i]);
+         }
+         Mmsg(tmp, "%s%s: %s", first?"":",", qkey.c_str(), qval.c_str());
+         send(ctx, tmp.c_str());
+         first = false;
+      }
+      send(ctx, "}");
+   }
+   send(ctx, "]\n");
+   return mdb->sql_num_rows();
 } 
  
 /* 
@@ -1164,7 +1230,6 @@ void TAG_DBR::gen_sql(JCR *jcr, BDB *db,
       aclbits |= DB_ACL_BIT(DB_ACL_JOB);
       aclbits_extra |= DB_ACL_BIT(DB_ACL_JOB);
    }
-
    if (*Name) {
       db->bdb_escape_string(jcr, esc_name, Name, strlen(Name));
    }
index a7de244ea9750cee2da6715accdea506cba57b29..24ef902c2830f810642e4a9bc9892bc97389a92f 100644 (file)
@@ -66,7 +66,7 @@ void BDB::bdb_list_pool_records(JCR *jcr, POOL_DBR *pdbr,
    bdb_lock();
    bdb_escape_string(jcr, esc, pdbr->Name, strlen(pdbr->Name));
 
-   if (type == VERT_LIST) {
+   if (type == VERT_LIST || type == JSON_LIST) {
       if (pdbr->Name[0] != 0) {
          Mmsg(cmd, "SELECT PoolId,Name,NumVols,MaxVols,UseOnce,UseCatalog,"
             "AcceptAnyVolume,VolRetention,VolUseDuration,MaxVolJobs,MaxVolBytes,"
@@ -109,7 +109,7 @@ void BDB::bdb_list_pool_records(JCR *jcr, POOL_DBR *pdbr,
 void BDB::bdb_list_client_records(JCR *jcr, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type)
 {
    bdb_lock();
-   if (type == VERT_LIST) {
+   if (type == VERT_LIST || type == JSON_LIST) {
       Mmsg(cmd, "SELECT ClientId,Name,Uname,AutoPrune,FileRetention,"
          "JobRetention "
            "FROM Client %s ORDER BY ClientId", get_acl(DB_ACL_CLIENT, true));
@@ -180,6 +180,7 @@ void BDB::bdb_list_plugin_objects(JCR *jcr, OBJECT_DBR *obj_r, DB_LIST_HANDLER *
    }
 
    switch (type) {
+   case JSON_LIST:
    case VERT_LIST:
          Mmsg(cmd,
             "SELECT Object.ObjectId, Object.JobId, Object.Path, Object.Filename, Object.PluginName, Object.ObjectCategory, "
@@ -216,6 +217,7 @@ void BDB::bdb_list_plugin_objects_ids(JCR *jcr, char* id_list, DB_LIST_HANDLER *
    POOL_MEM msg;
 
    switch (type) {
+   case JSON_LIST:
    case VERT_LIST:
          Mmsg(cmd,
             "SELECT Object.ObjectId, Object.JobId, Object.Path, Object.Filename, Object.PluginName, Object.ObjectCategory, "
@@ -273,7 +275,7 @@ void BDB::bdb_list_restore_objects(JCR *jcr, ROBJECT_DBR *rr, DB_LIST_HANDLER *s
    }
 
    bdb_lock();
-   if (type == VERT_LIST) {
+   if (type == VERT_LIST || type == JSON_LIST) {
       Mmsg(cmd, "SELECT JobId, RestoreObjectId, ObjectName, "
            "PluginName, ObjectType "
            "FROM RestoreObject JOIN Job USING (JobId) WHERE JobId IN (%s) %s "
@@ -314,7 +316,7 @@ void BDB::bdb_list_media_records(JCR *jcr, MEDIA_DBR *mdbr,
    const char *where = get_acl(DB_ACL_POOL, false);
    const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_POOL)) : "";
 
-   if (type == VERT_LIST) {
+   if (type == VERT_LIST || type == JSON_LIST) {
       if (mdbr->VolumeName[0] != 0) {
          Mmsg(cmd, "SELECT MediaId,VolumeName,Slot,PoolId,"
             "MediaType,MediaTypeId,FirstWritten,LastWritten,LabelDate,VolJobs,"
@@ -402,7 +404,7 @@ void BDB::bdb_list_jobmedia_records(JCR *jcr, uint32_t JobId,
                                                    DB_ACL_BIT(DB_ACL_FILESET) |
                                                    DB_ACL_BIT(DB_ACL_CLIENT)) : "";
 
-   if (type == VERT_LIST) {
+   if (type == VERT_LIST || type == JSON_LIST) {
       if (JobId > 0) {                   /* do by JobId */
          Mmsg(cmd, "SELECT JobMediaId,JobId,Media.MediaId,Media.VolumeName,"
             "FirstIndex,LastIndex,StartFile,JobMedia.EndFile,StartBlock,"
@@ -465,7 +467,7 @@ void BDB::bdb_list_filemedia_records(JCR *jcr, uint32_t JobId, uint32_t FileInde
    }
    
    bdb_lock();
-   if (type == VERT_LIST) {
+   if (type == VERT_LIST || type == JSON_LIST) {
       Mmsg(cmd, "SELECT JobId,FileIndex,Media.MediaId,Media.VolumeName,"
            "BlockAddress,RecordNo,FileOffset "
            "FROM FileMedia,Media WHERE Media.MediaId=FileMedia.MediaId "
@@ -632,7 +634,7 @@ void BDB::bdb_list_joblog_records(JCR *jcr, uint32_t JobId,
                                                    DB_ACL_BIT(DB_ACL_FILESET) |
                                                    DB_ACL_BIT(DB_ACL_CLIENT)) : "";
 
-   if (type == VERT_LIST) {
+   if (type == VERT_LIST || type == JSON_LIST) {
       Mmsg(cmd, "SELECT Time,LogText FROM Log %s "
            "WHERE Log.JobId=%s %s ORDER BY LogId ASC",
            join,
@@ -748,6 +750,7 @@ alist *BDB::bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER *sendit,
    }
 
    switch (type) {
+   case JSON_LIST:
    case VERT_LIST:
       Mmsg(cmd,
            "SELECT JobId,Job,Job.Name,PurgedFiles,Type,Level,"
@@ -1014,7 +1017,7 @@ void BDB::bdb_list_snapshot_records(JCR *jcr, SNAPSHOT_DBR *sdbr,
       pm_strcat(filter, " ORDER BY SnapshotId DESC");
    }
 
-   if (type == VERT_LIST || type == ARG_LIST) {
+   if (type == VERT_LIST || type == ARG_LIST || type == JSON_LIST) {
       Mmsg(cmd, "SELECT SnapshotId, Snapshot.Name, CreateDate, Client.Name AS Client, "
            "FileSet.FileSet AS FileSet, JobId, Volume, Device, Type, Retention, Comment "
            "FROM Snapshot JOIN Client USING (ClientId) LEFT JOIN FileSet USING (FileSetId) %s %s", filter, where);
index f8f06eb0102bd90ebedff864628a713a2b429da0..d876b0e9b0c764ababaa61a0f5a66be8379006ef 100644 (file)
@@ -43,6 +43,7 @@ extern int quit_cmd(UAContext *ua, const char *cmd);
 extern int qhelp_cmd(UAContext *ua, const char *cmd);
 extern bool dot_status_cmd(UAContext *ua, const char *cmd);
 extern void bvfs_set_acl(UAContext *ua, Bvfs *bvfs);
+extern bool jlist_cmd(UAContext *ua, const char *cmd);
 
 /* Forward referenced functions */
 static bool admin_cmds(UAContext *ua, const char *cmd);
@@ -110,6 +111,7 @@ static struct cmdstruct commands[] = { /* help */  /* can be used in runscript *
  { NT_(".help"),       dot_help_cmd,             NULL,       false},
  { NT_(".jobs"),       jobscmd,                  NULL,       true},
  { NT_(".estimate"),   dotestimatecmd,           NULL,       false},
+ { NT_(".jlist"),      jlist_cmd,                NULL,       false},
  { NT_(".levels"),     levelscmd,                NULL,       false},
  { NT_(".messages"),   getmsgscmd,               NULL,       false},
  { NT_(".msgs"),       msgscmd,                  NULL,       false},
index 9f48352524342f11d2c2930210bf592dddc217e9..a0912914c77e9712add08a9b2216d21f13b342e1 100644 (file)
@@ -335,6 +335,11 @@ bail_out:
  */
 
 /* Do long or full listing */
+bool jlist_cmd(UAContext *ua, const char *cmd)
+{
+   return do_list_cmd(ua, cmd, JSON_LIST);
+}
+
 int llist_cmd(UAContext *ua, const char *cmd)
 {
    return do_list_cmd(ua, cmd, VERT_LIST);
@@ -359,8 +364,9 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
    POOL_DBR pr;
    MEDIA_DBR mr;
 
-   if (!open_new_client_db(ua))
+   if (!open_new_client_db(ua)) {
       return 1;
+   }
 
    bmemset(&jr, 0, sizeof(jr));
    bmemset(&pr, 0, sizeof(pr));
@@ -407,9 +413,6 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
          } else if (B_ISALPHA(ua->argv[j][0])) {
             jr.JobLevel = ua->argv[j][0]; /* TODO: Check if the code is correct */
          }
-      } else if (strcasecmp(ua->argk[j], NT_("level")) == 0) {
-
-
       } else if (strcasecmp(ua->argk[j], NT_("client")) == 0) {
          if (is_name_valid(ua->argv[j], NULL)) {
             CLIENT_DBR cr;
@@ -435,10 +438,15 @@ static int do_list_cmd(UAContext *ua, const char *cmd, e_list_type llist)
       /* List JOBID=nn */
       } else if (strcasecmp(ua->argk[i], NT_("jobid")) == 0) {
          if (ua->argv[i]) {
-            jobid = str_to_int64(ua->argv[i]);
-            if (jobid > 0) {
-               jr.JobId = jobid;
-               db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
+            /* .jlist joblog jobid=1   should display only one json struct */
+            if (llist == JSON_LIST && i > 1) {
+               /* nop */
+            } else {
+               jobid = str_to_int64(ua->argv[i]);
+               if (jobid > 0) {
+                  jr.JobId = jobid;
+                  db_list_job_records(ua->jcr, ua->db, &jr, prtit, ua, llist);
+               }
             }
          }