bool bdb_get_client_pool(JCR *jcr, alist *results);
/* sql_list.c */
+ bool bdb_search_media_records(JCR *jcr, MEDIA_DBR *mdbr,
+ DB_RESULT_HANDLER *handler, void *ctx);
+ bool bdb_search_client_records(JCR *jcr, CLIENT_DBR *rec,
+ DB_RESULT_HANDLER *callback, void *ctx);
+ bool bdb_search_job_records(JCR *jcr, JOB_DBR *rec,
+ DB_RESULT_HANDLER *callback, void *ctx);
+ bool bdb_search_tag_records(JCR *jcr, TAG_DBR *rec,
+ DB_RESULT_HANDLER *callback, void *ctx);
+
void bdb_list_pool_records(JCR *jcr, POOL_DBR *pr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type);
alist *bdb_list_job_records(JCR *jcr, JOB_DBR *jr, DB_LIST_HANDLER sendit, void *ctx, e_list_type type);
void bdb_list_jobs_for_file(JCR *jcr, const char *client, const char *fname, DB_LIST_HANDLER *sendit, void *ctx, e_list_type type);
char sid[30]; /* edited StorageId */
bool set_first_written;
bool set_label_date;
+ int limit; /* Limit the number of records returned with search */
};
#define MAX_UNAME_LENGTH 256
char Name[MAX_NAME_LENGTH]; /* Client name */
char Uname[MAX_UNAME_LENGTH]; /* Uname for client */
char Plugins[MAX_PLUGIN_LENGTH]; /* Plugin list for this client */
+
+ int limit; /* used by search */
};
/* Counter record as in database */
JobId_t JobId; /* JobId */
bool all;
+ int limit; /* Used in search */
- TAG_DBR() {};
+ TAG_DBR() {
+ zero();
+ };
void zero() {
- JobId = 0;
+ limit = JobId = 0;
all = false;
*Object = *Client = *Job = *Pool = *Volume = *Comment = *Name = '\0';
};
mdb->bdb_get_snapshot_record(jcr, sr)
#define db_get_job_statistics(jcr, mdb, jr) \
mdb->bdb_get_job_statistics(jcr, jr)
+
/* sql_list.c */
#define db_list_jobs_for_file(jcr, mdb, cli, fname, result_handler, ctx, type) \
mdb->bdb_list_jobs_for_file(jcr, cli, fname, result_handler, ctx, type)
-#define db_list_pool_records(jcr, mdb, pr, sendit, ctx, type) \
+#define db_search_tag_records(jcr, mdb, tag, result_handler, ctx) \
+ mdb->bdb_search_tag_records(jcr, tag, result_handler, ctx)
+#define db_search_media_records(jcr, mdb, rec, result_handler, ctx) \
+ mdb->bdb_search_media_records(jcr, rec, result_handler, ctx)
+#define db_search_job_records(jcr, mdb, rec, result_handler, ctx) \
+ mdb->bdb_search_job_records(jcr, rec, result_handler, ctx)
+#define db_search_client_records(jcr, mdb, rec, result_handler, ctx) \
+ mdb->bdb_search_client_records(jcr, rec, result_handler, ctx)
+#define db_list_pool_records(jcr, mdb, pr, sendit, ctx, type) \
mdb->bdb_list_pool_records(jcr, pr, sendit, ctx, type)
#define db_list_job_records(jcr, mdb, jr, sendit, ctx, type) \
mdb->bdb_list_job_records(jcr, jr, sendit, ctx, type)
* -----------------------------------------------------------------------
*/
+/* We search resources for a specific tag, we just return the resource name
+ * itself and the result can be stored in a list for example
+ */
+bool BDB::bdb_search_tag_records(JCR *jcr, TAG_DBR *tag, DB_RESULT_HANDLER *result_handler, void *ctx)
+{
+ POOL_MEM tmp, where;
+ char esc[MAX_ESCAPE_NAME_LENGTH];
+ char esc_name[MAX_ESCAPE_NAME_LENGTH];
+ uint64_t aclbits, aclbits_extra;
+
+ const char *name;
+ const char *id;
+ const char *table;
+
+ tag->gen_sql(jcr, this, &table, &name, &id, esc, esc_name, &aclbits, &aclbits_extra);
+
+ bdb_lock();
+ pm_strcpy(where, get_acls(aclbits, true)); /* get_acls() uses a specific object buffer */
+ const char *whereand = get_acls(aclbits, false);
+ const char *join = get_acl_join_filter(aclbits_extra);
+
+ if (table) {
+ if (tag->all) {
+ if (*esc_name) {
+ /* Display all resource for a tag */
+ Mmsg(tmp, "SELECT %s AS %s FROM Tag%s AS T JOIN %s USING (%s) %s WHERE T.Tag = '%s' %s",
+ name, table, table, table, id, join, esc_name, whereand);
+ }
+ }
+ if (tag->limit > 0) {
+ char ed1[50];
+ pm_strcat(cmd, " LIMIT ");
+ pm_strcat(cmd, edit_uint64(tag->limit, ed1));
+ }
+ if (!bdb_sql_query(tmp.c_str(), result_handler, ctx)) {
+ bdb_unlock();
+ return false;
+ }
+ }
+ bdb_unlock();
+ return true;
+}
+
+/*
+ * Search Job record(s) that match JOB_DBR, the result can be stored in a alist for example
+ *
+ */
+bool BDB::bdb_search_job_records(JCR *jcr, JOB_DBR *jr,
+ DB_RESULT_HANDLER *handler,
+ void *ctx)
+{
+ char esc[MAX_ESCAPE_NAME_LENGTH];
+ const char *join = "";
+ const char *where_tmp = "";
+
+ if (jr->Job[0] == 0) {
+ return false;
+ }
+
+ bdb_lock();
+ bdb_escape_string(jcr, esc, jr->Job, strlen(jr->Job));
+
+ /* The ACL can limit on two extra tables, Client and FileSet */
+ where_tmp = get_acls(DB_ACL_BIT(DB_ACL_CLIENT) |
+ DB_ACL_BIT(DB_ACL_FILESET),
+ 0);
+
+ if (*where_tmp) {
+ join = get_acl_join_filter(DB_ACL_BIT(DB_ACL_CLIENT) |
+ DB_ACL_BIT(DB_ACL_FILESET));
+ }
+
+ Mmsg(cmd,
+ "SELECT Job "
+ "FROM Job "
+ " %s WHERE Job.Job ILIKE '%%%s%%' %s", join, esc, where_tmp);
+
+ if (jr->limit > 0) {
+ char ed1[50];
+ pm_strcat(cmd, " LIMIT ");
+ pm_strcat(cmd, edit_uint64(jr->limit, ed1));
+ }
+
+ if (!bdb_sql_query(cmd, handler, ctx)) {
+ bdb_unlock();
+ return false;
+ }
+ bdb_unlock();
+ return true;
+}
+
+/* Search for a client, return only the name */
+bool BDB::bdb_search_client_records(JCR *jcr, CLIENT_DBR *rec, DB_RESULT_HANDLER *callback, void *ctx)
+{
+ char esc[MAX_ESCAPE_NAME_LENGTH];
+ const char *where_tmp = "";
+
+ bdb_lock();
+ bdb_escape_string(jcr, esc, rec->Name, strlen(rec->Name));
+
+ /* We can apply some ACLs for the Client table */
+ where_tmp = get_acls(DB_ACL_BIT(DB_ACL_CLIENT), 0);
+
+ Mmsg(cmd, "SELECT Name "
+ "FROM Client WHERE Name ILIKE '%%%s%%' %s",
+ esc, where_tmp);
+
+ if (rec->limit > 0) {
+ char ed1[50];
+ pm_strcat(cmd, " LIMIT ");
+ pm_strcat(cmd, edit_uint64(rec->limit, ed1));
+ }
+
+ if (!bdb_sql_query(cmd, callback, ctx)) {
+ bdb_unlock();
+ return false;
+ }
+ bdb_unlock();
+ return true;
+}
+
/*
* Submit general SQL query
*/
bdb_unlock();
}
-
/*
* List plugin objects types
*/
bdb_unlock();
}
+/*
+ * If VolumeName is non-zero, list the record for that Volume
+ */
+bool BDB::bdb_search_media_records(JCR *jcr, MEDIA_DBR *mdbr,
+ DB_RESULT_HANDLER *handler, void *ctx)
+{
+ char ed1[50];
+ char esc[MAX_ESCAPE_NAME_LENGTH];
+
+ if (mdbr->VolumeName[0] == 0) {
+ return false;
+ }
+
+ bdb_lock();
+ bdb_escape_string(jcr, esc, mdbr->VolumeName, strlen(mdbr->VolumeName));
+ const char *where = get_acl(DB_ACL_POOL, false);
+ const char *join = *where ? get_acl_join_filter(DB_ACL_BIT(DB_ACL_POOL)) : "";
+
+ if (mdbr->limit == 0) {
+ mdbr->limit = 50;
+ }
+
+ Mmsg(cmd, "SELECT VolumeName FROM Media %s WHERE Media.VolumeName ILIKE '%%%s%%' %s LIMIT %u",
+ join,
+ esc,
+ where,
+ mdbr->limit);
+
+ if (!bdb_sql_query(cmd, handler, ctx)) {
+ bdb_unlock();
+ return false;
+ }
+
+ sql_free_result();
+ bdb_unlock();
+ return true;
+}
+
/*
* If VolumeName is non-zero, list the record for that Volume
* otherwise, list the Volumes in the Pool specified by PoolId
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,"
"VolFiles,VolBlocks,VolParts,VolCloudParts,Media.CacheRetention,VolMounts,VolBytes,"
static bool dot_help_cmd(UAContext *ua, const char *cmd);
static bool dot_add_events(UAContext *ua, const char *cmd);
static int one_handler(void *ctx, int num_field, char **row);
+static bool dot_search(UAContext *ua, const char *cmd);
struct dcmd_struct { const char *key; bool (*func)(UAContext *ua, const char *cmd); const char *help;const bool use_in_rs;};
static struct dcmd_struct commands[] = { /* help */ /* can be used in runscript */
{ NT_(".pools"), poolscmd, NULL, true},
{ NT_(".quit"), dot_quit_cmd, NULL, false},
{ NT_(".putfile"), putfile_cmd, NULL, false}, /* use @putfile */
+ { NT_(".search"), dot_search, NULL, false},
{ NT_(".schedule"), schedulescmd, NULL, false},
{ NT_(".sql"), sql_cmd, NULL, false},
{ NT_(".status"), dot_status_cmd, NULL, false},
bail_out:
return true;
}
+
+enum {
+ CAT_VOLUME = (1<<0),
+ CAT_JOB = (1<<1),
+ CAT_CLIENT = (1<<2),
+};
+
+#define CAT_ALL (CAT_VOLUME | CAT_JOB | CAT_CLIENT)
+
+/* Search over our different tables
+ *
+ * {
+ * "error": 0,
+ * "errmsg", "",
+ * "type": "search",
+ * "data": {
+ * "volume": ["vol1", "vol2"],
+ * "client": ["volA"],
+ * "job": []
+ * }
+ * }
+ *
+ */
+static bool dot_search(UAContext *ua, const char *cmd)
+{
+ uint32_t cat = 0;
+ uint64_t limit=0;
+ char *text = NULL;
+ const char *errmsg = NULL;
+ alist res(10, owned_by_alist);
+ OutputWriter out(ua->api_opts);
+ alist *r = &res;
+
+ for(int i=1; i < ua->argc ; i++) {
+ if (strcasecmp(ua->argk[i], NT_("limit")) == 0 && is_a_number(ua->argv[i])) {
+ limit = str_to_uint64(ua->argv[i]);
+
+ } else if (strcasecmp(ua->argk[i], NT_("category")) == 0) {
+ if (strcasecmp(NPRTB(ua->argv[i]), "all") == 0) {
+ cat = CAT_ALL;
+
+ } else if (strcasecmp(NPRTB(ua->argv[i]), "client") == 0) {
+ cat |= CAT_CLIENT;
+
+ } else if (strcasecmp(NPRTB(ua->argv[i]), "job") == 0) {
+ cat |= CAT_JOB;
+
+ } else if (strcasecmp(NPRTB(ua->argv[i]), "volume") == 0) {
+ cat |= CAT_VOLUME;
+
+ } else {
+ errmsg = "Incorrect category";
+ goto bail_out;
+ }
+
+ } else if (strcasecmp(ua->argk[i], NT_("text")) == 0) {
+ text = ua->argv[i];
+
+ } else {
+ errmsg = "Incorrect parameter";
+ goto bail_out;
+ }
+ }
+ if (!text || strlen(text) <= 3) {
+ errmsg = "Missing text to search";
+ goto bail_out;
+ }
+
+ /* TODO: check for nasty characters in text */
+ if (!cat) {
+ cat = CAT_ALL;
+ }
+ if (!limit) {
+ limit = 10;
+ }
+ if (!open_new_client_db(ua)) {
+ errmsg = "Unable to open the catalog";
+ goto bail_out;
+ }
+
+ /* We initialize the output */
+ out.get_output(OT_START_OBJ,
+ OT_INT, "error", 0,
+ OT_STRING, "errmsg", "",
+ OT_STRING, "type", "search",
+ OT_END);
+ out.start_object("data");
+
+ if (text[0] == '#') {
+ /* search for a tag */
+ TAG_DBR tr;
+ r->destroy();
+ if (cat & CAT_CLIENT) {
+ tr.zero();
+ tr.all = true;
+ bstrncpy(tr.Name, text, sizeof(tr.Name));
+ strcpy(tr.Client, "1"); // To enable the Client search
+ if (!db_search_tag_records(ua->jcr, ua->db, &tr, db_string_list_handler, &r)) {
+ errmsg = "Unable to query client tag";
+ goto bail_out;
+ }
+ }
+ out.get_output(OT_ALIST_STR, "client", r, OT_END);
+
+ r->destroy();
+ if (cat & CAT_JOB) {
+ tr.zero();
+ tr.all = true;
+ bstrncpy(tr.Name, text, sizeof(tr.Name));
+ strcpy(tr.Job, "1"); // To enable the Job search
+ if (!db_search_tag_records(ua->jcr, ua->db, &tr, db_string_list_handler, &r)) {
+ errmsg = "Unable to query Job tag";
+ goto bail_out;
+ }
+ }
+ out.get_output(OT_ALIST_STR, "job", r, OT_END);
+
+ r->destroy();
+ if (cat & CAT_VOLUME) {
+ tr.zero();
+ tr.all = true;
+ bstrncpy(tr.Name, text, sizeof(tr.Name));
+ strcpy(tr.Volume, "1"); // To enable the Volume search
+ if (!db_search_tag_records(ua->jcr, ua->db, &tr, db_string_list_handler, &r)) {
+ errmsg = "Unable to query Volume tag";
+ goto bail_out;
+ }
+ }
+ out.get_output(OT_ALIST_STR, "volume", r, OT_END);
+
+ } else {
+ r->destroy();
+ if (cat & CAT_VOLUME) {
+ /* Search in volumes */
+ MEDIA_DBR mdbr;
+ bstrncpy(mdbr.VolumeName, text, sizeof(mdbr.VolumeName));
+ mdbr.limit = limit;
+ if (!db_search_media_records(ua->jcr, ua->db, &mdbr, db_string_list_handler, &r)) {
+ errmsg = "Unable to query Volume";
+ goto bail_out;
+ }
+ }
+ out.get_output(OT_ALIST_STR, "volume", r, OT_END);
+
+ /* Search in clients */
+ r->destroy();
+ if (cat & CAT_CLIENT) {
+ CLIENT_DBR cr;
+ memset(&cr, 0, sizeof(cr));
+ cr.limit = limit;
+ bstrncpy(cr.Name, text, sizeof(cr.Name));
+ if (!db_search_client_records(ua->jcr, ua->db, &cr, db_string_list_handler, &r)) {
+ errmsg = "Unable to query Client";
+ goto bail_out;
+ }
+ }
+ out.get_output(OT_ALIST_STR, "client", &res, OT_END);
+
+ /* Search in Job */
+ r->destroy();
+ if (cat & CAT_JOB) {
+ JOB_DBR jr;
+ memset(&jr, 0, sizeof(jr));
+ jr.limit = limit;
+ bstrncpy(jr.Job, text, sizeof(jr.Job));
+ if (!db_search_job_records(ua->jcr, ua->db, &jr, db_string_list_handler, &r)) {
+ errmsg = "Unable to query Job";
+ goto bail_out;
+ }
+ }
+ out.get_output(OT_ALIST_STR, "job", &res, OT_END);
+ }
+ out.end_object();
+ ua->send_msg(out.get_output(OT_END_OBJ, OT_END));
+ return true;
+
+bail_out:
+ ua->send_msg(out.get_output(OT_CLEAR,
+ OT_START_OBJ,
+ OT_INT, "error", 1,
+ OT_STRING, "errmsg", errmsg,
+ OT_END_OBJ, OT_END));
+ return true;
+}