/* Change this each time there is some incompatible
* file format change!!!!
*/
-#define BDB_VERSION 7 /* file version number */
+#define BDB_VERSION 8 /* file version number */
struct s_control {
int bdb_version; /* Version number */
*/
/* Job record */
typedef struct {
- uint32_t JobId;
+ JobId_t JobId;
char Job[MAX_NAME_LENGTH]; /* Job unique name */
char Name[MAX_NAME_LENGTH]; /* Job base name */
int Type; /* actually char(1) */
time_t SchedTime; /* Time job scheduled */
time_t StartTime; /* Job start time */
time_t EndTime; /* Job termination time */
+ btime_t StartDay; /* Start time/date in seconds */
uint32_t VolSessionId;
uint32_t VolSessionTime;
uint32_t JobFiles;
/* JobMedia record */
typedef struct {
uint32_t JobMediaId; /* record id */
- uint32_t JobId; /* JobId */
+ JobId_t JobId; /* JobId */
uint32_t MediaId; /* MediaId */
uint32_t FirstIndex; /* First index this Volume */
uint32_t LastIndex; /* Last index this Volume */
} JOBMEDIA_DBR;
-
-
/* Attributes record -- NOT same as in database because
* in general, this "record" creates multiple database
* records (e.g. pathname, filename, fileattributes).
char *attr; /* attributes statp */
uint32_t FileIndex;
uint32_t Stream;
- uint32_t JobId;
+ JobId_t JobId;
uint32_t ClientId;
uint32_t PathId;
uint32_t FilenameId;
typedef struct {
FileId_t FileId;
uint32_t FileIndex;
- uint32_t JobId;
+ JobId_t JobId;
uint32_t FilenameId;
uint32_t PathId;
char LStat[256];
int UseCatalog; /* set to use catalog */
int AcceptAnyVolume; /* set to accept any volume sequence */
int AutoRecycle; /* set to recycle automatically */
- uint32_t VolumeRetention; /* retention period in seconds */
+ int Recycle; /* default Vol recycle flag */
+ btime_t VolumeRetention; /* retention period in seconds */
char PoolType[MAX_NAME_LENGTH];
char LabelFormat[MAX_NAME_LENGTH];
/* Extra stuff not in DB */
uint64_t VolBytes; /* Number of bytes written */
uint64_t VolMaxBytes; /* max bytes to write */
uint64_t VolCapacityBytes; /* capacity estimate */
+ btime_t VolRetention; /* Volume retention in seconds */
+ int Recycle; /* recycle yes/no */
char VolStatus[20]; /* Volume status */
- char Recycle[20]; /* Recycle yes/no */
/* Extra stuff not in DB */
faddr_t rec_addr; /* found record address */
} MEDIA_DBR;
/* Client record -- same as the database */
typedef struct {
uint32_t ClientId; /* Unique Client id */
+ int AutoPrune;
+ btime_t FileRetention;
+ btime_t JobRetention;
char Name[MAX_NAME_LENGTH]; /* Client name */
char Uname[256]; /* Uname for client */
} CLIENT_DBR;
VolCapacityBytes BIGINT UNSIGNED NOT NULL,
VolStatus ENUM('Full', 'Archive', 'Append', 'Recycle', 'Purged',
'Read-Only', 'Disabled', 'Error', 'Busy') NOT NULL,
- Recycle ENUM('No', 'Yes') NOT NULL,
+ Recycle TINYINT NOT NULL,
+ VolRetention BIGINT UNSIGNED NOT NULL,
PRIMARY KEY(MediaId),
INDEX (PoolId)
);
UseOnce TINYINT NOT NULL,
UseCatalog TINYINT NOT NULL,
AcceptAnyVolume TINYINT NOT NULL,
+ VolRetention BIGINT NOT NULL,
+ AutoRecycle TINYINT NOT NULL,
+ Recycle TINYINT NOT NULL,
PoolType ENUM('Backup', 'Copy', 'Cloned', 'Archive', 'Migration') NOT NULL,
LabelFormat TINYBLOB,
UNIQUE (Name(128)),
ClientId INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
Name TINYBLOB NOT NULL,
Uname TINYBLOB NOT NULL, /* full uname -a of client */
+ AutoPrune TINYINT NOT NULL,
+ FileRetention BIGINT NOT NULL,
+ JobRetention BIGINT NOT NULL,
UNIQUE (Name(128)),
PRIMARY KEY(ClientId)
);
+CREATE TABLE Version (
+ VersionId INTEGER UNSIGNED NOT NULL
+ );
+
+-- Initialize Version
+INSERT INTO Version (VersionId) VALUES (1);
+
+
## Experimental
#CREATE TABLE FileSave (
# FileSaveId INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
SchedTime DATETIME NOT NULL,
StartTime DATETIME DEFAULT 0,
EndTime DATETIME DEFAULT 0,
- StartDay INTEGER UNSIGNED DEFAULT 0,
+ StartDay BIGINT UNSIGNED DEFAULT 0,
VolSessionId INTEGER UNSIGNED DEFAULT 0,
VolSessionTime INTEGER UNSIGNED DEFAULT 0,
JobFiles INTEGER UNSIGNED DEFAULT 0,
VolMaxBytes BIGINT UNSIGNED DEFAULT 0,
VolCapacityBytes BIGINT UNSIGNED DEFAULT 0,
VolStatus VARCHAR(20) NOT NULL,
- Recycle VARCHAR(20) NOT NULL,
+ Recycle TINYINT NOT NULL,
+ VolRetention BIGINT UNSIGNED NOT NULL,
PRIMARY KEY(MediaId)
);
UseOnce TINYINT NOT NULL,
UseCatalog TINYINT NOT NULL,
AcceptAnyVolume TINYINT NOT NULL,
+ VolRetention BIGINT NOT NULL,
+ AutoRecycle TINYINT NOT NULL,
+ Recycle TINYINT NOT NULL,
PoolType VARCHAR(20) NOT NULL,
LabelFormat VARCHAR(128) NOT NULL,
UNIQUE (Name),
ClientId INTEGER UNSIGNED AUTOINCREMENT,
Name VARCHAR(128) NOT NULL,
Uname VARCHAR(255) NOT NULL, -- uname -a field
+ AutoPrune TINYINT NOT NULL,
+ FileRetention BIGINT NOT NULL,
+ JobRetention BIGINT NOT NULL,
UNIQUE (Name),
PRIMARY KEY(ClientId)
);
-- Initialize JobId to start at 1
INSERT INTO NextId (id, TableName) VALUES (1, "Job");
+CREATE TABLE Version (
+ VersionId INTEGER UNSIGNED NOT NULL
+ );
+
+-- Initialize Version
+INSERT INTO Version (VersionId) VALUES (1);
+
+
-- Experimental stuff below. Not used.
-- Invariant part of File
CREATE TABLE BaseFile (
V(mutex);
return 0;
}
+
+ if (!check_tables_version(mdb)) {
+ V(mutex);
+ return 0;
+ }
+
mdb->connected = TRUE;
V(mutex);
return 1;
int get_sql_record_max(B_DB *mdb);
char *db_next_index(B_DB *mdb, char *table);
int db_sql_query(B_DB *mdb, char *cmd, DB_RESULT_HANDLER *result_handler, void *ctx);
+int check_tables_version(B_DB *mdb);
/* create.c */
int db_create_file_attributes_record(B_DB *mdb, ATTR_DBR *ar);
* calling subroutine sets and clears the mutex
*/
+/* Check that the tables conrrespond to the version we want */
+int check_tables_version(B_DB *mdb)
+{
+/*****FIXME***** implement */
+ return 1;
+}
+
/* Utility routine for queries */
int
QueryDB(char *file, int line, B_DB *mdb, char *cmd)
db_create_pool_record(B_DB *mdb, POOL_DBR *pool_dbr)
{
int stat;
+ char ed1[30];
P(mdb->mutex);
Mmsg(&mdb->cmd, "SELECT PoolId,Name FROM Pool WHERE Name=\"%s\"", pool_dbr->Name);
/* Must create it */
Mmsg(&mdb->cmd,
"INSERT INTO Pool (Name, NumVols, MaxVols, UseOnce, UseCatalog, \
-AcceptAnyVolume, PoolType, LabelFormat) \
-VALUES (\"%s\", %d, %d, %d, %d, %d, \"%s\", \"%s\")",
+AcceptAnyVolume, AutoRecycle, Recycle, VolumeRetention, PoolType, LabelFormat) \
+VALUES (\"%s\", %d, %d, %d, %d, %d, %d, %d, %s \"%s\", \"%s\")",
pool_dbr->Name,
pool_dbr->NumVols, pool_dbr->MaxVols,
pool_dbr->UseOnce, pool_dbr->UseCatalog,
pool_dbr->AcceptAnyVolume,
+ pool_dbr->AutoRecycle, pool_dbr->Recycle,
+ edit_uint64(pool_dbr->VolumeRetention, ed1),
pool_dbr->PoolType, pool_dbr->LabelFormat);
if (!INSERT_DB(mdb, mdb->cmd)) {
db_create_media_record(B_DB *mdb, MEDIA_DBR *mr)
{
int stat;
- char ed1[30], ed2[30];
+ char ed1[30], ed2[30], ed3[30];
P(mdb->mutex);
Mmsg(&mdb->cmd, "SELECT MediaId FROM Media WHERE VolumeName=\"%s\"",
/* Must create it */
Mmsg(&mdb->cmd,
"INSERT INTO Media (VolumeName, MediaType, PoolId, VolMaxBytes, VolCapacityBytes, \
-VolStatus, Recycle) VALUES (\"%s\", \"%s\", %d, %s, %s, \"%s\", \"%s\")",
+VolStatus, Recycle) VALUES (\"%s\", \"%s\", %d, %s, %s, %d, %s, \"%s\")",
mr->VolumeName,
mr->MediaType, mr->PoolId,
edit_uint64(mr->VolMaxBytes,ed1),
edit_uint64(mr->VolCapacityBytes, ed2),
- mr->VolStatus, mr->Recycle);
+ mr->Recycle,
+ edit_uint64(mr->VolRetention, ed3),
+ mr->VolStatus);
if (!INSERT_DB(mdb, mdb->cmd)) {
Mmsg2(&mdb->errmsg, _("Create DB Media record %s failed. ERR=%s\n"),
{
SQL_ROW row;
int stat;
+ char ed1[30], ed2[30];
P(mdb->mutex);
Mmsg(&mdb->cmd, "SELECT ClientId FROM Client WHERE Name=\"%s\"", cr->Name);
}
/* Must create it */
- Mmsg(&mdb->cmd, "INSERT INTO Client (Name, Uname) VALUES \
-(\"%s\", \"%s\")", cr->Name, cr->Uname);
+ Mmsg(&mdb->cmd, "INSERT INTO Client (Name, Uname, AutoPrune, \
+FileRetention, JobRetention) VALUES \
+(\"%s\", \"%s\")", cr->Name, cr->Uname, cr->AutoPrune,
+ edit_uint64(cr->FileRetention, ed1),
+ edit_uint64(cr->JobRetention, ed2));
if (!INSERT_DB(mdb, mdb->cmd)) {
Mmsg2(&mdb->errmsg, _("Create DB Client record %s failed. ERR=%s\n"),
P(mdb->mutex);
if (jr->JobId == 0) {
Mmsg(&mdb->cmd, "SELECT VolSessionId, VolSessionTime, \
-PoolId, StartTime, EndTime, JobFiles, JobBytes, Job \
+PoolId, StartTime, EndTime, JobFiles, JobBytes, StartDay, Job \
FROM Job WHERE Job=\"%s\"", jr->Job);
} else {
Mmsg(&mdb->cmd, "SELECT VolSessionId, VolSessionTime, \
-PoolId, StartTime, EndTime, JobFiles, JobBytes, Job \
+PoolId, StartTime, EndTime, JobFiles, JobBytes, StartDay, Job \
FROM Job WHERE JobId=%d", jr->JobId);
}
strcpy(jr->cEndTime, row[4]);
jr->JobFiles = atol(row[5]);
jr->JobBytes = (uint64_t)strtod(row[6], NULL);
- strcpy(jr->Job, row[7]);
+ jr->StartDay = (btime_t)strtod(row[7], NULL);
+ strcpy(jr->Job, row[8]);
sql_free_result(mdb);
V(mdb->mutex);
return 0;
}
free(db_name);
+ if (!check_tables_version(mdb)) {
+ V(mutex);
+ return 0;
+ }
+
mdb->connected = TRUE;
V(mutex);
return 1;
/* Forward referenced functions */
static void terminate_console(int sig);
-int get_cmd(char *prompt, BSOCK *sock, int sec);
+int get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec);
/* Static variables */
static char *configfile = NULL;
exit(1);
}
+static void read_and_process_input(FILE *input, BSOCK *UA_sock)
+{
+ char *prompt = "*";
+ int at_prompt = FALSE;
+ int tty_input = isatty(fileno(input));
+ int stat;
+
+ for ( ;; ) {
+ if (at_prompt) { /* don't prompt multiple times */
+ prompt = "";
+ } else {
+ prompt = "*";
+ at_prompt = TRUE;
+ }
+ if (tty_input) {
+ stat = get_cmd(input, prompt, UA_sock, 30);
+ } else {
+ int len = sizeof_pool_memory(UA_sock->msg) - 1;
+ if (fgets(UA_sock->msg, len, input) == NULL) {
+ stat = -1;
+ } else {
+ strip_trailing_junk(UA_sock->msg);
+ UA_sock->msglen = strlen(UA_sock->msg);
+ stat = 1;
+ }
+ }
+ if (stat < 0) {
+ break; /* error */
+ } else if (stat == 0) { /* timeout */
+ bnet_fsend(UA_sock, ".messages");
+ } else {
+ at_prompt = FALSE;
+ if (!bnet_send(UA_sock)) { /* send command */
+ break; /* error */
+ }
+ }
+ if (strcmp(UA_sock->msg, "quit") == 0 || strcmp(UA_sock->msg, "exit") == 0) {
+ break;
+ }
+ while ((stat = bnet_recv(UA_sock)) > 0) {
+ if (at_prompt) {
+ fprintf(output, "\n");
+ at_prompt = FALSE;
+ }
+ printf("%s", UA_sock->msg);
+ }
+ fflush(output);
+ if (stat < 0) {
+ break; /* error */
+ } else if (stat == 0) {
+ if (UA_sock->msglen == BNET_PROMPT) {
+ at_prompt = TRUE;
+ }
+ Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
+ }
+ }
+}
+
/*********************************************************************
*
*/
int main(int argc, char *argv[])
{
- int ch, stat, i, ndir, item;
+ int ch, i, ndir, item;
int no_signals = FALSE;
int test_config = FALSE;
JCR jcr;
- char *prompt = "*";
- int at_prompt = FALSE;
init_stack_dump();
my_name_is(argc, argv, "console");
dir->DIRport);
}
UnlockRes();
- if (get_cmd("Select Director: ", UA_sock, 600) < 0) {
+ if (get_cmd(stdin, "Select Director: ", UA_sock, 600) < 0) {
return 1;
}
item = atoi(UA_sock->msg);
Dmsg0(40, "Opened connection with Director daemon\n");
- for ( ;; ) {
- if (at_prompt) { /* don't prompt multiple times */
- prompt = "";
- } else {
- prompt = "*";
- at_prompt = TRUE;
- }
- stat = get_cmd(prompt, UA_sock, 30);
- if (stat < 0) {
- break; /* error */
- } else if (stat == 0) { /* timeout */
- bnet_fsend(UA_sock, ".messages");
- } else {
- at_prompt = FALSE;
- if (!bnet_send(UA_sock)) { /* send command */
- break; /* error */
- }
- }
- if (strcmp(UA_sock->msg, "quit") == 0 || strcmp(UA_sock->msg, "exit") == 0) {
- break;
- }
- while ((stat = bnet_recv(UA_sock)) > 0) {
- if (at_prompt) {
- printf("\n");
- at_prompt = FALSE;
- }
- printf("%s", UA_sock->msg);
- }
- fflush(stdout);
- if (stat < 0) {
- break; /* error */
- } else if (stat == 0) {
- if (UA_sock->msglen == BNET_PROMPT) {
- at_prompt = TRUE;
- }
- Dmsg1(100, "Got poll %s\n", bnet_sig_to_ascii(UA_sock));
- }
- }
+ read_and_process_input(stdin, UA_sock);
+
if (UA_sock) {
bnet_sig(UA_sock, BNET_TERMINATE); /* send EOF */
bnet_close(UA_sock);
#include "readline/readline.h"
#include "readline/history.h"
+
int
-get_cmd(char *prompt, BSOCK *sock, int sec)
+get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
{
char *line;
* -1 if EOF or error
*/
int
-get_cmd(char *prompt, BSOCK *sock, int sec)
+get_cmd(FILE *input, char *prompt, BSOCK *sock, int sec)
{
+ int len;
fprintf(output, prompt);
fflush(output);
- switch (wait_for_data(fileno(stdin), sec)) {
+ switch (wait_for_data(fileno(input), sec)) {
case 0:
return 0; /* timeout */
case -1:
return -1; /* error */
default:
- if (fgets(sock->msg, 200, stdin) == NULL) {
+ len = sizeof_pool_memory(sock->msg) - 1;
+ if (fgets(sock->msg, len, input) == NULL) {
return -1;
}
break;
#include "bacula.h"
#include "dird.h"
+#include "ua.h"
/* Commands sent to File daemon */
static char backupcmd[] = "backup\n";
*/
memset(&cr, 0, sizeof(cr));
strcpy(cr.Name, jcr->client->hdr.name);
+ cr.AutoPrune = jcr->client->AutoPrune;
+ cr.FileRetention = jcr->client->FileRetention;
+ cr.JobRetention = jcr->client->JobRetention;
if (jcr->client_name) {
free(jcr->client_name);
}
*/
static struct res_items pool_items[] = {
{"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
- {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
+ {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
{"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
- {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
+ {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
{"usecatalog", store_yesno, ITEM(res_pool.use_catalog), 1, ITEM_DEFAULT, 1},
- {"usevolumeonce", store_yesno, ITEM(res_pool.use_volume_once), 1, 0, 0},
+ {"usevolumeonce", store_yesno, ITEM(res_pool.use_volume_once), 1, 0, 0},
{"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
- {"acceptanyvolume", store_yesno, ITEM(res_pool.accept_any_volume), 1, 0, 0},
+ {"acceptanyvolume", store_yesno, ITEM(res_pool.accept_any_volume), 1, 0, 0},
{"catalogfiles", store_yesno, ITEM(res_pool.catalog_files), 1, ITEM_DEFAULT, 1},
{"volumeretention", store_time, ITEM(res_pool.VolumeRetention), 0, ITEM_DEFAULT, 60*60*24*365},
- {"autorecycle", store_yesno, ITEM(res_pool.AutoRecycle), 0, ITEM_DEFAULT, 1},
+ {"autorecycle", store_yesno, ITEM(res_pool.AutoRecycle), 1, ITEM_DEFAULT, 1},
+ {"recycle", store_yesno, ITEM(res_pool.Recycle), 1, ITEM_DEFAULT, 1},
{NULL, NULL, NULL, 0, 0, 0}
};
int accept_any_volume; /* accept any volume */
int max_volumes; /* max number of volumes */
btime_t VolumeRetention; /* volume retention period in seconds */
- int AutoRecycle; /* auto recycle */
+ int AutoRecycle; /* default for pool auto recycle */
+ int Recycle; /* default for media recycle yes/no */
};
typedef struct s_res_pool POOL;
strcat(name, "%04d");
sprintf(mr.VolumeName, name, ++pr.NumVols);
strcpy(mr.VolStatus, "Append");
- strcpy(mr.Recycle, "No");
+ mr.Recycle = pr.Recycle;
+ mr.VolRetention = pr.VolumeRetention;
if (db_create_media_record(jcr->db, &mr) &&
db_update_pool_record(jcr->db, &pr) == 1) {
Dmsg1(90, "Created new Volume=%s\n", mr.VolumeName);
*/
memset(&cr, 0, sizeof(cr));
strcpy(cr.Name, jcr->client->hdr.name);
+ cr.AutoPrune = jcr->client->AutoPrune;
+ cr.FileRetention = jcr->client->FileRetention;
+ cr.JobRetention = jcr->client->JobRetention;
if (jcr->client_name) {
free(jcr->client_name);
}
JCR *jcr;
B_DB *db;
CAT *catalog;
- char *cmd; /* return command/name buffer */
- char *args; /* command line arguments */
- char *argk[MAX_ARGS]; /* argument keywords */
- char *argv[MAX_ARGS]; /* argument values */
- int argc; /* number of arguments */
- char **prompt; /* list of prompts */
- int max_prompts; /* max size of list */
- int num_prompts; /* current number in list */
- int auto_display_messages; /* if set, display messages */
+ char *cmd; /* return command/name buffer */
+ char *args; /* command line arguments */
+ char *argk[MAX_ARGS]; /* argument keywords */
+ char *argv[MAX_ARGS]; /* argument values */
+ int argc; /* number of arguments */
+ char **prompt; /* list of prompts */
+ int max_prompts; /* max size of list */
+ int num_prompts; /* current number in list */
+ int auto_display_messages; /* if set, display messages */
int user_notified_msg_pending; /* set when user notified */
- int automount; /* if set, mount after label */
+ int automount; /* if set, mount after label */
+ int quit; /* if set, quit */
+ int verbose; /* set for normal UA verbosity */
} UAContext;
/* ua_cmds.c */
void start_prompt(UAContext *ua, char *msg);
void add_prompt(UAContext *ua, char *prompt);
int do_prompt(UAContext *ua, char *msg, char *prompt);
-CAT *get_catalog_resource(UAContext *ua);
+CAT *get_catalog_resource(UAContext *ua);
STORE *get_storage_resource(UAContext *ua, char *cmd);
int get_media_type(UAContext *ua, char *MediaType);
int get_pool_dbr(UAContext *ua, POOL_DBR *pr);
mr.PoolId = pr.PoolId;
strcpy(mr.VolStatus, "Append");
- strcpy(mr.Recycle, "No");
+ mr.Recycle = pr.Recycle;
+ mr.VolRetention = pr.VolumeRetention;
for (i=startnum; i < num+startnum; i++) {
sprintf(mr.VolumeName, name, i);
Dmsg1(200, "Create Volume %s\n", mr.VolumeName);
return 1;
}
mr.PoolId = pr.PoolId;
- strcpy(mr.Recycle, "Yes");
strcpy(mr.VolStatus, "Append");
+ mr.Recycle = pr.Recycle;
+ mr.VolRetention = pr.VolumeRetention;
ua->jcr->store = store;
bsendmsg(ua, _("Connecting to Storage daemon %s at %s:%d ...\n"),
mr.VolumeName);
if (ua->automount) {
bsendmsg(ua, _("Requesting mount %s ...\n"), dev_name);
+ bash_spaces(dev_name);
bnet_fsend(sd, "mount %s", dev_name);
+ unbash_spaces(dev_name);
while (bnet_recv(sd) > 0) {
bsendmsg(ua, "%s", sd->msg);
/* Here we can get
int quitcmd(UAContext *ua, char *cmd)
{
- return 0;
+ ua->quit = TRUE;
+ return 1;
}
static int helpcmd(UAContext *ua, char *cmd)
BSOCK *sock = ua->UA_sock;
ua->cmd[0] = 0;
+ if (!sock) { /* No UA */
+ return 0;
+ }
bnet_fsend(sock, "%s", prompt);
bnet_sig(sock, BNET_PROMPT); /* request more input */
for ( ;; ) {
}
-/*
- * Callback routine for "printing" database file listing
- */
-void prtit(void *ctx, char *msg)
-{
- UAContext *ua = (UAContext *)ctx;
-
- bnet_fsend(ua->UA_sock, "%s", msg);
-}
-
-/* Format message and send to other end */
-void bsendmsg(void *ctx, char *fmt, ...)
-{
- va_list arg_ptr;
- UAContext *ua = (UAContext *)ctx;
- BSOCK *bs = ua->UA_sock;
- int maxlen;
-
-again:
- maxlen = sizeof_pool_memory(bs->msg) - 1;
- va_start(arg_ptr, fmt);
- bs->msglen = bvsnprintf(bs->msg, maxlen, fmt, arg_ptr);
- va_end(arg_ptr);
- if (bs->msglen < 0 || bs->msglen >= maxlen) {
- bs->msg = (char *) realloc_pool_memory(bs->msg, maxlen + 200);
- goto again;
- }
- bnet_send(bs);
-}
/*
}
return 1;
}
+
+/*
+ * Callback routine for "printing" database file listing
+ */
+void prtit(void *ctx, char *msg)
+{
+ UAContext *ua = (UAContext *)ctx;
+
+ bnet_fsend(ua->UA_sock, "%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 Bacula core. In
+ * that case direct the messages to the Job.
+ */
+void bsendmsg(void *ctx, char *fmt, ...)
+{
+ va_list arg_ptr;
+ UAContext *ua = (UAContext *)ctx;
+ BSOCK *bs = ua->UA_sock;
+ int maxlen, len;
+ char *msg;
+
+ if (bs) {
+ msg = bs->msg;
+ } else {
+ msg = (char *)get_pool_memory(PM_EMSG);
+ }
+
+again:
+ maxlen = sizeof_pool_memory(msg) - 1;
+ va_start(arg_ptr, fmt);
+ len = bvsnprintf(msg, maxlen, fmt, arg_ptr);
+ va_end(arg_ptr);
+ if (len < 0 || len >= maxlen) {
+ msg = (char *) realloc_pool_memory(msg, maxlen + 200);
+ goto again;
+ }
+
+ if (bs) {
+ bs->msglen = len;
+ bnet_send(bs);
+ } else { /* No UA, send to Job */
+ Jmsg(ua->jcr, M_INFO, 0, msg);
+ free_memory(msg);
+ }
+
+}
int prune_files(UAContext *ua, CLIENT *client);
int prune_jobs(UAContext *ua, CLIENT *client);
int prune_volume(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
+static int mark_media_purged(UAContext *ua, MEDIA_DBR *mr);
#define MAX_DEL_LIST_LEN 1000000
/*
* Prune records from database
+ *
+ * prune files (from) client=xxx
+ * prune jobs (from) client=xxx
+ * prune volume=xxx
*/
int prunecmd(UAContext *ua, char *cmd)
{
char ed1[50];
memset(&cr, 0, sizeof(cr));
+ memset(&del, 0, sizeof(del));
strcpy(cr.Name, client->hdr.name);
if (!db_create_client_record(ua->db, &cr)) {
return 0;
today = (uint64_t)(date_encode(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday) -
date_encode(2000, 1, 1));
- del.JobId = NULL;
- del.num_ids = 0;
- del.tot_ids = 0;
- del.num_del = 0;
- del.max_ids = 0;
-
Dmsg3(100, "Today=%d period=%d period=%d\n", (uint32_t)today, (uint32_t)period,
(uint32_t)(period/(3600*24)));
char *query = (char *)get_pool_memory(PM_MESSAGE);
int i;
struct tm tm;
- uint64_t today, period;
+ btime_t today, period;
time_t now;
CLIENT_DBR cr;
char ed1[50];
memset(&cr, 0, sizeof(cr));
+ memset(&del, 0, sizeof(del));
strcpy(cr.Name, client->hdr.name);
if (!db_create_client_record(ua->db, &cr)) {
return 0;
period = client->JobRetention;
now = time(NULL);
localtime_r(&now, &tm);
- today = (uint64_t)(date_encode(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday) -
+ today = (btime_t)(date_encode(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday) -
date_encode(2000, 1, 1));
- del.JobId = NULL;
- del.num_ids = 0;
- del.tot_ids = 0;
- del.num_del = 0;
- del.max_ids = 0;
-
Dmsg3(050, "Today=%d period=%d period=%d\n", (uint32_t)today, (uint32_t)period,
(uint32_t)(period/(3600*24)));
{
char *query = (char *)get_pool_memory(PM_MESSAGE);
struct s_count_ctx cnt;
+ struct s_file_del_ctx del;
+ int i;
+ JOB_DBR jr;
+ struct tm tm;
+ btime_t today, period;
+ time_t now;
+ memset(&jr, 0, sizeof(jr));
+ memset(&del, 0, sizeof(del));
+ cnt.count = 0;
Mmsg(&query, "SELECT count(*) FROM JobMedia WHERE MediaId=%d", mr->MediaId);
if (!db_sql_query(ua->db, query, count_handler, (void *)&cnt)) {
bsendmsg(ua, "%s", db_strerror(ua->db));
if (cnt.count == 0) {
bsendmsg(ua, "There are no Jobs associated with Volume %s. It is purged.\n",
mr->VolumeName);
+ if (!mark_media_purged(ua, mr)) {
+ goto bail_out;
+ }
+ goto bail_out;
+ }
+
+ if (cnt.count < MAX_DEL_LIST_LEN) {
+ del.max_ids = cnt.count + 1;
} else {
- bsendmsg(ua, "There are still %d Jobs on Volume %s. It is not purged.\n",
- cnt.count, mr->VolumeName);
+ del.max_ids = MAX_DEL_LIST_LEN;
}
+
+ del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);
+
+ Mmsg(&query, "SELECT JobId FROM JobMedia WHERE MediaId=%d", mr->MediaId);
+ if (!db_sql_query(ua->db, query, file_delete_handler, (void *)&del)) {
+ bsendmsg(ua, "%s", db_strerror(ua->db));
+ Dmsg0(050, "Count failed\n");
+ goto bail_out;
+ }
+
+ /* Use Volume Retention to purge Jobs and Files */
+ period = mr->VolRetention;
+ period = 30 * 3600 *24; /* ****FIXME***** remove */
+ now = time(NULL);
+ localtime_r(&now, &tm);
+ today = (btime_t)(date_encode(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday) -
+ date_encode(2000, 1, 1));
+
+ Dmsg3(050, "Today=%d period=%d period=%d\n", (uint32_t)today, (uint32_t)period,
+ (uint32_t)(period/(3600*24)));
+
+ for (i=0; i < del.num_ids; i++) {
+ jr.JobId = del.JobId[i];
+ if (!db_get_job_record(ua->db, &jr)) {
+ continue;
+ }
+ if (jr.StartDay >= (today - period/(3600*24))) {
+ continue;
+ }
+ Dmsg1(050, "Delete JobId=%d\n", del.JobId[i]);
+ Mmsg(&query, "DELETE FROM File WHERE JobId=%d", del.JobId[i]);
+ db_sql_query(ua->db, query, NULL, (void *)NULL);
+ Mmsg(&query, "DELETE FROM Job WHERE JobId=%d", del.JobId[i]);
+ db_sql_query(ua->db, query, NULL, (void *)NULL);
+ Mmsg(&query, "DELETE FROM JobMedia WHERE JobId=%d", del.JobId[i]);
+ db_sql_query(ua->db, query, NULL, (void *)NULL);
+ Dmsg1(050, "Del sql=%s\n", query);
+ del.num_del++;
+ }
+ if (del.JobId) {
+ free(del.JobId);
+ }
+ bsendmsg(ua, _("%d Jobs on Volume %s pruned from catalog.\n"), del.num_del,
+ mr->VolumeName);
+
+ /* If purged, mark it so */
+ if (del.num_ids == del.num_del) {
+ mark_media_purged(ua, mr);
+ }
+
bail_out:
free_pool_memory(query);
return 1;
}
+
+static int mark_media_purged(UAContext *ua, MEDIA_DBR *mr)
+{
+ if (strcmp(mr->VolStatus, "Append") == 0 ||
+ strcmp(mr->VolStatus, "Full") == 0) {
+ strcpy(mr->VolStatus, "Purged");
+ if (!db_update_media_record(ua->db, mr)) {
+ bsendmsg(ua, "%s", db_strerror(ua->db));
+ return 0;
+ }
+ }
+ return 1;
+}
void purge_files_from_volume(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr );
void purge_jobs_from_volume(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
void purge_files_from_job(UAContext *ua, JOB_DBR *jr);
+static int mark_media_purged(UAContext *ua, MEDIA_DBR *mr);
#define MAX_DEL_LIST_LEN 1000000
int count;
};
+/*
+ * Called here to count entries to be deleted
+ */
+static int count_handler(void *ctx, int num_fields, char **row)
+{
+ struct s_count_ctx *cnt = (struct s_count_ctx *)ctx;
+
+ if (row[0]) {
+ cnt->count = atoi(row[0]);
+ } else {
+ cnt->count = 0;
+ }
+ return 0;
+}
/*
* Called here to count entries to be deleted
return 0;
}
-void purge_files_from_volume(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr ) {} /* ***FIXME*** implement */
-void purge_jobs_from_volume(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr) {} /* ***FIXME*** implement */
-
-
-
/*
* Purge records from database
*
- * Purge Files from [Job|JobId|Client|Volume]
- * Purge Jobs from [Client|Volume]
+ * Purge Files (from) [Job|JobId|Client|Volume]
+ * Purge Jobs (from) [Client|Volume]
*
* N.B. Not all above is implemented yet.
*/
NULL};
bsendmsg(ua, _(
- "This command is DANGEROUUS!\n"
+ "This command is DANGEROUS!!!\n"
"It purges (deletes) all Files from a Job,\n"
"JobId, Client or Volume; or it purges (deletes)\n"
"all Jobs from a Client or Volume. Normally you\n"
CLIENT_DBR cr;
memset(&cr, 0, sizeof(cr));
+ memset(&del, 0, sizeof(del));
+
strcpy(cr.Name, client->hdr.name);
if (!db_create_client_record(ua->db, &cr)) {
return 0;
}
- del.JobId = NULL;
- del.num_ids = 0;
- del.tot_ids = 0;
- del.num_del = 0;
- del.max_ids = 0;
-
Mmsg(&query, select_jobsfiles_from_client, cr.ClientId);
Dmsg1(050, "select sql=%s\n", query);
CLIENT_DBR cr;
memset(&cr, 0, sizeof(cr));
+ memset(&del, 0, sizeof(del));
+
strcpy(cr.Name, client->hdr.name);
if (!db_create_client_record(ua->db, &cr)) {
return 0;
}
- del.JobId = NULL;
- del.num_ids = 0;
- del.tot_ids = 0;
- del.num_del = 0;
- del.max_ids = 0;
-
Mmsg(&query, select_jobs_from_client, cr.ClientId);
Dmsg1(050, "select sql=%s\n", query);
del.tot_ids = 0;
-
-
del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);
del.PurgedFiles = (char *)malloc(del.max_ids);
free_pool_memory(query);
}
+
+void purge_files_from_volume(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr )
+{} /* ***FIXME*** implement */
+
+void purge_jobs_from_volume(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr)
+{
+ char *query = (char *)get_pool_memory(PM_MESSAGE);
+ struct s_count_ctx cnt;
+ struct s_file_del_ctx del;
+ int i;
+ JOB_DBR jr;
+
+ memset(&jr, 0, sizeof(jr));
+ memset(&del, 0, sizeof(del));
+ cnt.count = 0;
+ Mmsg(&query, "SELECT count(*) FROM JobMedia WHERE MediaId=%d", mr->MediaId);
+ if (!db_sql_query(ua->db, query, count_handler, (void *)&cnt)) {
+ bsendmsg(ua, "%s", db_strerror(ua->db));
+ Dmsg0(050, "Count failed\n");
+ goto bail_out;
+ }
+
+ if (cnt.count == 0) {
+ bsendmsg(ua, "There are no Jobs associated with Volume %s. It is purged.\n",
+ mr->VolumeName);
+ if (!mark_media_purged(ua, mr)) {
+ goto bail_out;
+ }
+ goto bail_out;
+ }
+
+ if (cnt.count < MAX_DEL_LIST_LEN) {
+ del.max_ids = cnt.count + 1;
+ } else {
+ del.max_ids = MAX_DEL_LIST_LEN;
+ }
+
+ del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);
+
+ Mmsg(&query, "SELECT JobId FROM JobMedia WHERE MediaId=%d", mr->MediaId);
+ if (!db_sql_query(ua->db, query, file_delete_handler, (void *)&del)) {
+ bsendmsg(ua, "%s", db_strerror(ua->db));
+ Dmsg0(050, "Count failed\n");
+ goto bail_out;
+ }
+
+ for (i=0; i < del.num_ids; i++) {
+ Dmsg1(050, "Delete JobId=%d\n", del.JobId[i]);
+ Mmsg(&query, "DELETE FROM File WHERE JobId=%d", del.JobId[i]);
+ db_sql_query(ua->db, query, NULL, (void *)NULL);
+ Mmsg(&query, "DELETE FROM Job WHERE JobId=%d", del.JobId[i]);
+ db_sql_query(ua->db, query, NULL, (void *)NULL);
+ Mmsg(&query, "DELETE FROM JobMedia WHERE JobId=%d", del.JobId[i]);
+ db_sql_query(ua->db, query, NULL, (void *)NULL);
+ Dmsg1(050, "Del sql=%s\n", query);
+ del.num_del++;
+ }
+ if (del.JobId) {
+ free(del.JobId);
+ }
+ bsendmsg(ua, _("%d Files for Volume %s purged from catalog.\n"), del.num_del,
+ mr->VolumeName);
+
+ /* If purged, mark it so */
+ if (del.num_ids == del.num_del) {
+ mark_media_purged(ua, mr);
+ }
+
+bail_out:
+ free_pool_memory(query);
+}
+
+static int mark_media_purged(UAContext *ua, MEDIA_DBR *mr)
+{
+ if (strcmp(mr->VolStatus, "Append") == 0 ||
+ strcmp(mr->VolStatus, "Full") == 0) {
+ strcpy(mr->VolStatus, "Purged");
+ if (!db_update_media_record(ua->db, mr)) {
+ bsendmsg(ua, "%s", db_strerror(ua->db));
+ return 0;
+ }
+ }
+ return 1;
+}
} else {
sprintf(pmsg, "%s (1-%d): ", msg, ua->num_prompts-1);
}
- if (!get_cmd(ua, pmsg) || *ua->cmd == '.') {
+ /* Either a . or an @ will get you out of the loop */
+ if (!get_cmd(ua, pmsg) || *ua->cmd == '.' || *ua->cmd == '@') {
item = -1; /* error */
break;
}
*/
static void handle_UA_client_request(void *arg)
{
- int quit, stat;
+ int stat;
static char cmd[1000];
UAContext ua;
BSOCK *UA_sock = (BSOCK *) arg;
memset(&ua, 0, sizeof(ua));
ua.automount = TRUE;
+ ua.verbose = TRUE;
ua.jcr = new_jcr(sizeof(JCR), dird_free_jcr);
close_msg(ua.jcr); /* we don't handle messages */
ua.jcr->sd_auth_key = bstrdup("dummy"); /* dummy Storage daemon key */
goto getout;
}
- quit = FALSE;
- while (!quit) {
+ while (!ua.quit) {
stat = bnet_recv(ua.UA_sock);
if (stat > 0) {
strncpy(cmd, ua.UA_sock->msg, sizeof(cmd));
cmd[sizeof(cmd)-1] = 0; /* ensure it is terminated/trucated */
parse_command_args(&ua);
if (ua.argc > 0 && ua.argk[0][0] == '.') {
- quit = !do_a_dot_command(&ua, cmd);
+ do_a_dot_command(&ua, cmd);
} else {
- quit = !do_a_command(&ua, cmd);
+ do_a_command(&ua, cmd);
}
- if (!quit) {
+ if (!ua.quit) {
if (ua.auto_display_messages) {
strcpy(cmd, "messages");
qmessagescmd(&ua, cmd);
}
} else if (stat == 0) {
if (ua.UA_sock->msglen == BNET_TERMINATE) {
- quit = TRUE;
+ ua.quit = TRUE;
break;
}
bnet_sig(ua.UA_sock, BNET_POLL);
CLIENT_DBR cr;
memset(&cr, 0, sizeof(cr));
+ cr.AutoPrune = jcr->client->AutoPrune;
+ cr.FileRetention = jcr->client->FileRetention;
+ cr.JobRetention = jcr->client->JobRetention;
strcpy(cr.Name, jcr->client->hdr.name);
if (jcr->client_name) {
free(jcr->client_name);