#if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL
#include "cats.h"
+
+static int dbglvl=100;
void append_filter(POOLMEM **buf, char *cond)
{
}
}
-void OBJECT_DBR::parse_plugin_object_string(char **obj_str)
+/* Parse stream of tags, return next one from the stream (it will be null terminated,
+ * original buffer will be changed) */
+static char *get_next_tag(char **buf)
{
- /* TODO: Do some sanity checks on the input format */
- char *p = *obj_str;
- int fnl, pnl;
- char* tmp_path = p;
- skip_nonspaces(&p); /* skip Path */
+ char *p = *buf;
+ char *tmp = p;
+ skip_nonspaces(&p);
skip_spaces(&p);
- char *c = strpbrk(tmp_path, " ");
+ char *c = strpbrk(tmp, " ");
if (c) {
*c = '\0';
+ } else {
+ Dmsg0(dbglvl, "No tag found!\n");
+ return NULL;
}
+ *buf = p;
+
+ Dmsg1(dbglvl, "Found tag: %s\n", tmp);
+ return tmp;
+}
+
+
+bool OBJECT_DBR::parse_plugin_object_string(char **obj_str)
+{
+ bool ret = false;
+ int fnl, pnl;
- if (tmp_path[strlen(tmp_path) - 1] == '/') {
- pm_strcpy(Path, tmp_path);
+ char *tmp = get_next_tag(obj_str);
+ if (!tmp) {
+ goto bail_out;
+ }
+
+ if (tmp[strlen(tmp) - 1] == '/') {
+ pm_strcpy(Path, tmp);
unbash_spaces(Path);
} else {
- split_path_and_filename(tmp_path, &Path, &pnl, &Filename, &fnl);
+ split_path_and_filename(tmp, &Path, &pnl, &Filename, &fnl);
unbash_spaces(Path);
unbash_spaces(Filename);
}
- char *tmp = p;
- skip_nonspaces(&p); /* skip PluginName */
- skip_spaces(&p);
- c = strpbrk(tmp, " ");
- if (c) {
- *c = '\0';
+ tmp = get_next_tag(obj_str);
+ if (!tmp) {
+ goto bail_out;
}
pm_strcpy(PluginName, tmp);
unbash_spaces(PluginName);
- tmp = p;
- skip_nonspaces(&p); /* skip ObjectType */
- skip_spaces(&p);
- c = strpbrk(tmp, " ");
- if (c) {
- *c = '\0';
+ tmp = get_next_tag(obj_str);
+ if (!tmp) {
+ goto bail_out;
}
bstrncpy(ObjectCategory, tmp, sizeof(ObjectCategory));
unbash_spaces(ObjectCategory);
- tmp = p;
- skip_nonspaces(&p); /* skip ObjectType */
- skip_spaces(&p);
- c = strpbrk(tmp, " ");
- if (c) {
- *c = '\0';
+ tmp = get_next_tag(obj_str);
+ if (!tmp) {
+ goto bail_out;
}
bstrncpy(ObjectType, tmp, sizeof(ObjectType));
unbash_spaces(ObjectType);
- tmp = p;
- skip_nonspaces(&p); /* skip ObjectName */
- skip_spaces(&p);
- c = strpbrk(tmp, " ");
- if (c) {
- *c = '\0';
+ tmp = get_next_tag(obj_str);
+ if (!tmp) {
+ goto bail_out;
}
bstrncpy(ObjectName, tmp, sizeof(ObjectName));
unbash_spaces(ObjectName);
- tmp = p;
- skip_nonspaces(&p); /* skip ObjectSource */
- skip_spaces(&p);
- c = strpbrk(tmp, " ");
- if (c) {
- *c = '\0';
+ tmp = get_next_tag(obj_str);
+ if (!tmp) {
+ goto bail_out;
}
bstrncpy(ObjectSource, tmp, sizeof(ObjectSource));
unbash_spaces(ObjectSource);
- tmp = p;
- skip_nonspaces(&p); /* skip ObjectUuid */
- skip_spaces(&p);
- c = strpbrk(tmp, " ");
- if (c) {
- *c = '\0';
+ tmp = get_next_tag(obj_str);
+ if (!tmp) {
+ goto bail_out;
}
bstrncpy(ObjectUUID, tmp, sizeof(ObjectUUID));
unbash_spaces(ObjectUUID);
- ObjectSize = str_to_uint64(p);
+ tmp = get_next_tag(obj_str);
+ if (tmp) {
+ ObjectSize = str_to_uint64(tmp);
+ } else if (*obj_str) {
+ /* Object size is the last tag here, we are not expecting to have status in the stream */
+ ObjectSize = str_to_uint64(*obj_str);
+ ret = true;
+ goto bail_out;
+ } else {
+ goto bail_out;
+ }
+
+ /* We should have status string in the end */
+ if (*obj_str) {
+ tmp = get_next_tag(obj_str);
+ if (!tmp) {
+ goto bail_out;
+ }
+ ObjectStatus = (int)*tmp;
+ if (*obj_str) {
+ ObjectCount = str_to_uint64(*obj_str);
+ }
+ } else {
+ goto bail_out;
+ }
+
+ ret = true;
+
+bail_out:
+ if (!ret) {
+ /* Reset parsed fields */
+ reset();
+ }
+
+ return ret;
}
void OBJECT_DBR::create_db_filter(JCR *jcr, POOLMEM **where)
Mmsg(tmp, " Object.ObjectSize=%llu", ObjectSize);
append_filter(where, tmp.c_str());
}
+
+ if (ObjectStatus != 0) {
+ Mmsg(tmp, " Object.ObjectStatus='%c'", ObjectStatus);
+ append_filter(where, tmp.c_str());
+ }
}
}
len = strlen(robj_r->object_name);
robj_r->object = &robj_r->object_name[len+1]; /* point to object */
robj_r->object[robj_r->object_len] = 0; /* add zero for those who attempt printing */
- Dmsg7(100, "oname=%s stream=%d FT=%d FI=%d JobId=%ld, obj_len=%d\nobj=\"%s\"\n",
+ Dmsg7(dbglvl, "oname=%s stream=%d FT=%d FI=%d JobId=%ld, obj_len=%d\nobj=\"%s\"\n",
robj_r->object_name, robj_r->Stream, robj_r->FileType, robj_r->FileIndex, robj_r->JobId,
robj_r->object_len, robj_r->object);
}
class OBJECT_DBR {
public:
- OBJECT_DBR() {
+ OBJECT_DBR() {
Path = get_pool_memory(PM_FNAME);
Filename = get_pool_memory(PM_FNAME);
PluginName = get_pool_memory(PM_FNAME);
reset();
};
- ~OBJECT_DBR() {
+
+ ~OBJECT_DBR() {
free_pool_memory(Path);
free_pool_memory(Filename);
free_pool_memory(PluginName);
};
+
/* reset memory */
void reset() {
JobId = 0;
*ObjectCategory = *ObjectType = *ObjectName = *ObjectSource = *ObjectUUID = *ClientName = 0;
limit = 0;
order = 0;
+ ObjectStatus = 'U';
+ ObjectCount = 0;
};
+
/* Parse OBJECT record from stream */
- void parse_plugin_object_string(char **obj_str);
+ bool parse_plugin_object_string(char **obj_str);
/* Helper for creating the 'where' part of jcr's related sql query based on fields from the Object */
void create_db_filter(JCR *jcr, POOLMEM **where);
char ObjectSource[MAX_NAME_LENGTH];
char ObjectUUID[MAX_NAME_LENGTH];
uint64_t ObjectSize;
+ int32_t ObjectStatus;
+ uint32_t ObjectCount;
/* Fields not stored in db directly */
char ClientName[MAX_NAME_LENGTH];
(
ObjectId BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
- JobId INTEGER UNSIGNED not null,
+ JobId INTEGER UNSIGNED NOT NULL,
Path BLOB NOT NULL,
Filename BLOB NOT NULL,
PluginName TINYBLOB NOT NULL,
ObjectSource TINYBLOB NOT NULL,
ObjectUUID TINYBLOB NOT NULL,
ObjectSize bigint NOT NULL,
+ ObjectStatus BINARY(1) NOT NULL DEFAULT 'U',
+ ObjectCount INTEGER UNSIGNED NOT NULL DEFAULT 1,
primary key (ObjectId)
);
ObjectSource text not null,
ObjectUUID text not null,
ObjectSize bigint not null,
+ ObjectStatus char(1) not null default 'U',
+ ObjectCount integer not null default 1,
primary key (ObjectId)
);
ObjectSource text not null,
ObjectUUID text not null,
ObjectSize integer not null,
+ ObjectStatus char(1) not null default 'U',
+ ObjectCount integer not null default 1,
primary key (ObjectId)
);
bdb_lock();
Mmsg(cmd,
"INSERT INTO Object (JobId, Path, Filename, PluginName, ObjectCategory, "
- "ObjectType, ObjectName, ObjectSource, ObjectUUID, ObjectSize) "
- "VALUES (%lu, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %llu)",
+ "ObjectType, ObjectName, ObjectSource, ObjectUUID, ObjectSize, ObjectStatus, ObjectCount) "
+ "VALUES (%lu, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %llu, '%c', %lu)",
obj->JobId, esc_path, esc_filename, esc_plugin_name, esc_obj_category,
esc_obj_type, esc_obj_name, esc_obj_source, esc_obj_uuid,
- obj->ObjectSize);
+ obj->ObjectSize, (char)obj->ObjectStatus, obj->ObjectCount);
obj->ObjectId = sql_insert_autokey_record(cmd, NT_("Object"));
if (obj->ObjectId == 0) {
Mmsg(cmd,
"SELECT ObjectId, JobId, Path, Filename, PluginName, ObjectCategory, "
- "ObjectType, ObjectName, ObjectSource, ObjectUUID, ObjectSize "
+ "ObjectType, ObjectName, ObjectSource, ObjectUUID, ObjectSize, ObjectStatus, ObjectCount "
"FROM Object %s", where.c_str());
bdb_lock();
bstrncpy(obj_r->ObjectSource, row[8], sizeof(obj_r->ObjectSource));
bstrncpy(obj_r->ObjectUUID, row[9], sizeof(obj_r->ObjectUUID));
obj_r->ObjectSize = str_to_uint64(row[10]);
+ obj_r->ObjectStatus = row[11] != NULL ? (int)*row[11] : 'U';
+ obj_r->ObjectCount = str_to_uint64(row[12]);
stat = true;
}
case VERT_LIST:
Mmsg(cmd,
"SELECT Object.ObjectId, Object.JobId, Object.Path, Object.Filename, Object.PluginName, Object.ObjectCategory, "
- "Object.ObjectType, Object.ObjectName, Object.ObjectSource, Object.ObjectUUID, Object.ObjectSize "
+ "Object.ObjectType, Object.ObjectName, Object.ObjectSource, "
+ "Object.ObjectUUID, Object.ObjectSize, Object.ObjectStatus, Object.ObjectCount "
"FROM Object %s %s", join.c_str(), where.c_str());
break;
case HORZ_LIST:
Mmsg(cmd,
"SELECT Object.ObjectId, Object.JobId, Object.ObjectCategory, "
- "Object.ObjectType, Object.ObjectName "
+ "Object.ObjectType, Object.ObjectName, Object.ObjectStatus "
"FROM Object %s %s", join.c_str(), where.c_str());
break;
default:
case VERT_LIST:
Mmsg(cmd,
"SELECT Object.ObjectId, Object.JobId, Object.Path, Object.Filename, Object.PluginName, Object.ObjectCategory, "
- "Object.ObjectType, Object.ObjectName, Object.ObjectSource, Object.ObjectUUID, Object.ObjectSize "
+ "Object.ObjectType, Object.ObjectName, Object.ObjectSource, Object.ObjectUUID, "
+ "Object.ObjectSize, Object.ObjectStatus, Object.ObjectCount "
"FROM Object WHERE ObjectId IN (%s) ORDER BY ObjectId ASC", id_list);
break;
case HORZ_LIST:
Mmsg(cmd,
"SELECT Object.ObjectId, Object.JobId, Object.ObjectCategory, "
- "Object.ObjectType, Object.ObjectName, Object.ObjectUUID "
+ "Object.ObjectType, Object.ObjectName, Object.ObjectUUID, Object.ObjectStatus "
"FROM Object WHERE ObjectId IN (%s) ORDER BY ObjectId ASC", id_list);
break;
default:
} else if (Stream == STREAM_PLUGIN_OBJECT) {
OBJECT_DBR obj_r;
- obj_r.parse_plugin_object_string(&p);
+ if (!obj_r.parse_plugin_object_string(&p)) {
+ Jmsg0(jcr, M_FATAL, 0, _("Failed to parse plugin object!\n"));
+ return;
+ }
if (jcr->wjcr) {
obj_r.JobId = jcr->wjcr->JobId;
"\tfilemedia jobid=<nn> fileindex=<mm> | clients\n"
"\tevents [type=<str> | limit=<int> | order=<asc|desc> | days=<int> | start=<time-specification> | end=<time-specification> |\n"
"\t\t source=<str> | code=<str> | type=<str> ]\n"
- "\tobjects [jobid=<jobid> client=<cli> type=<str> | category=<str> | limit=<int> | order=<asc|desc> ]\n"
+ "\tobjects [jobid=<jobid> client=<cli> type=<str> | category=<str> | status=<S> | limit=<int> | order=<asc|desc> ]\n"
), false},
{ NT_("llist"), llist_cmd, _("Full or long list like list command"),
* list nextvol job=xx - list the next vol to be used by job
* list nextvolume job=xx - same as above.
* list copies jobid=x,y,z
- * list objects [type=objecttype job_id=id clientname=n] - list plugin objects
+ * list objects [type=objecttype job_id=id clientname=n,status=S] - list plugin objects
* list pluginrestoreconf jobid=x,y,z [id=k]
* list filemedia jobid=x fileindex=z
*
} else if (strcasecmp(ua->argk[j], NT_("category")) == 0) {
bstrncpy(obj_r.ObjectCategory, ua->argv[j], sizeof(obj_r.ObjectCategory));
+ } else if (strcasecmp(ua->argk[j], NT_("status")) == 0) {
+ obj_r.ObjectStatus = (int32_t)ua->argv[j][0];
} else if (strcasecmp(ua->argk[j], NT_("limit")) == 0 && ua->argv[j]) {
obj_r.limit = atoi(ua->argv[j]);
bash_spaces(object_source.c_str());
pm_strcpy(object_uuid, NPRTB(ff_pkt->plugin_obj.object_uuid));
bash_spaces(object_uuid.c_str());
- sd->msglen = Mmsg(sd->msg, "%s %s %s %s %s %s %s %llu%c",
+ sd->msglen = Mmsg(sd->msg, "%s %s %s %s %s %s %s %llu %c %lu%c",
path.c_str(), plugin_name.c_str(), object_category.c_str(), object_type.c_str(),
object_name.c_str(), object_source.c_str(), object_uuid.c_str(),
- ff_pkt->plugin_obj.object_size, 0);
+ ff_pkt->plugin_obj.object_size,
+ /* Plugin can left status unset, we need to handle that */
+ ff_pkt->plugin_obj.status != 0 ? (char)ff_pkt->plugin_obj.status : 'U',
+ ff_pkt->plugin_obj.count,
+ 0);
stat = sd->send();
break;
ff_pkt->plugin_obj.object_source = sp.plugin_obj.object_source;
ff_pkt->plugin_obj.object_uuid = sp.plugin_obj.object_uuid;
ff_pkt->plugin_obj.object_size = sp.plugin_obj.object_size;
+ ff_pkt->plugin_obj.status = sp.plugin_obj.status;
+ ff_pkt->plugin_obj.count = sp.plugin_obj.count;
} else {
ff_pkt->restore_obj.object_name = sp.restore_obj.object_name;
ff_pkt->restore_obj.index = sp.restore_obj.index; /* restore object index */
//TODO probably more specific db types needed
#define PLUGIN_OBJECT_DATABASE "database" /* For all database plugins */
+/* Possible statuses of plugin objects. Based on Job status */
+#define PLUG_OBJ_STATUS_UNSET 'U' /* Status was not set at all by the plugin */
+#define PLUG_OBJ_STATUS_TERMINATED 'T' /* Plugin object is OK */
+#define PLUG_OBJ_STATUS_WARNING 'W' /* Plugin object OK, with some non-fatal warning */
+#define PLUG_OBJ_STATUS_ERROR 'e' /* Non-fatal error */
+#define PLUG_OBJ_STATUS_FATAL 'f' /* Fatal error occured */
+
struct plugin_object {
char *path;
char *plugin_name;
char *object_source;
char *object_uuid;
uint64_t object_size;
+ int32_t status;
+ uint32_t count;
};
enum metadata_type {
sp->plugin_obj.object_name = (char *)NT_("test vm");
sp->plugin_obj.object_source = (char *)NT_("test plugin source");
sp->plugin_obj.object_uuid = (char *)NT_("1234-abc-testplugin");
+ sp->plugin_obj.status = PLUG_OBJ_STATUS_TERMINATED;
+ sp->plugin_obj.count = 2;
sp->plugin_obj.object_size = obj_uuid++;
sp->type = FT_PLUGIN_OBJECT;
char *buf = rec->data;
num_plugin_objects++;
- obj_r.parse_plugin_object_string(&buf);
+ if (!obj_r.parse_plugin_object_string(&buf)) {
+ Pmsg0(000, _("Failed to parse plugin object!\n"));
+ break;
+ }
// Need to get new jobId if possible
mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);