*
* Version $Id$
*/
-
/*
- Copyright (C) 2002-2005 Kern Sibbald
+ Copyright (C) 2002-2006 Kern Sibbald
This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of
- the License, or (at your option) any later version.
+ modify it under the terms of the GNU General Public License
+ version 2 as amended with additional clauses defined in the
+ file LICENSE in the main source directory.
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
- General Public License for more details.
-
- You should have received a copy of the GNU General Public
- License along with this program; if not, write to the Free
- Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- MA 02111-1307, USA.
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ the file LICENSE for additional details.
*/
+
#include "bacula.h"
#include "dird.h"
extern char *uar_jobid_fileindex, *uar_dif, *uar_sel_all_temp;
extern char *uar_count_files, *uar_jobids_fileindex;
extern char *uar_jobid_fileindex_from_dir;
+extern char *uar_jobid_fileindex_from_table;
-struct NAME_LIST {
- char **name; /* list of names */
- int num_ids; /* ids stored */
- int max_ids; /* size of array */
- int num_del; /* number deleted */
- int tot_ids; /* total to process */
-};
-
-
-/* Main structure for obtaining JobIds or Files to be restored */
-struct RESTORE_CTX {
- utime_t JobTDate;
- uint32_t TotalFiles;
- uint32_t JobId;
- char ClientName[MAX_NAME_LENGTH];
- char last_jobid[10];
- POOLMEM *JobIds; /* User entered string of JobIds */
- STORE *store;
- JOB *restore_job;
- POOL *pool;
- int restore_jobs;
- uint32_t selected_files;
- char *where;
- RBSR *bsr;
- POOLMEM *fname; /* filename only */
- POOLMEM *path; /* path only */
- POOLMEM *query;
- int fnl; /* filename length */
- int pnl; /* path length */
- bool found;
- bool all; /* mark all as default */
- NAME_LIST name_list;
-};
-
-
-#define MAX_ID_LIST_LEN 1000000
-
/* Forward referenced functions */
static int last_full_handler(void *ctx, int num_fields, char **row);
static int jobid_handler(void *ctx, int num_fields, char **row);
-static int get_next_jobid_from_list(char **p, uint32_t *JobId);
static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx);
static int fileset_handler(void *ctx, int num_fields, char **row);
static void print_name_list(UAContext *ua, NAME_LIST *name_list);
static int get_client_name(UAContext *ua, RESTORE_CTX *rx);
static int get_date(UAContext *ua, char *date, int date_len);
static int count_handler(void *ctx, int num_fields, char **row);
+static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table);
/*
* Restore files
RESTORE_CTX rx; /* restore context */
JOB *job;
int i;
+ JCR *jcr = ua->jcr;
memset(&rx, 0, sizeof(rx));
rx.path = get_pool_memory(PM_FNAME);
}
if (rx.bsr->JobId) {
+ uint32_t selected_files;
if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */
bsendmsg(ua, _("Unable to construct a valid BSR. Cannot continue.\n"));
goto bail_out;
}
- if (!(rx.selected_files = write_bsr_file(ua, rx.bsr))) {
+ if (!(selected_files = write_bsr_file(ua, rx))) {
bsendmsg(ua, _("No files selected to be restored.\n"));
goto bail_out;
}
- bsendmsg(ua, _("\n%u file%s selected to be restored.\n\n"), rx.selected_files,
- rx.selected_files==1?"":"s");
+ /* If no count of files, use bsr generated value (often wrong) */
+ if (rx.selected_files == 0) {
+ rx.selected_files = selected_files;
+ }
+ if (rx.selected_files==1) {
+ bsendmsg(ua, _("\n1 file selected to be restored.\n\n"));
+ }
+ else {
+ bsendmsg(ua, _("\n%u files selected to be restored.\n\n"), rx.selected_files);
+ }
} else {
bsendmsg(ua, _("No files selected to be restored.\n"));
goto bail_out;
/* Build run command */
if (rx.where) {
Mmsg(ua->cmd,
- "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
+ "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
" where=\"%s\" files=%d catalog=\"%s\"",
job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
- working_directory, rx.where, rx.selected_files, ua->catalog->hdr.name);
+ jcr->RestoreBootstrap, rx.where, rx.selected_files, ua->catalog->hdr.name);
} else {
Mmsg(ua->cmd,
- "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
+ "run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s\""
" files=%d catalog=\"%s\"",
job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
- working_directory, rx.selected_files, ua->catalog->hdr.name);
+ jcr->RestoreBootstrap, rx.selected_files, ua->catalog->hdr.name);
}
- if (find_arg(ua, _("yes")) > 0) {
+ if (find_arg(ua, NT_("yes")) > 0) {
pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */
}
- Dmsg1(400, "Submitting: %s\n", ua->cmd);
+ Dmsg1(100, "Submitting: %s\n", ua->cmd);
parse_ua_args(ua);
run_cmd(ua, ua->cmd);
free_rx(&rx);
free_name_list(&rx->name_list);
}
+static bool has_value(UAContext *ua, int i)
+{
+ if (!ua->argv[i]) {
+ bsendmsg(ua, _("Missing value for keyword: %s\n"), ua->argk[i]);
+ return false;
+ }
+ return true;
+}
+
static int get_client_name(UAContext *ua, RESTORE_CTX *rx)
{
/* If no client name specified yet, get it now */
if (!rx->ClientName[0]) {
CLIENT_DBR cr;
/* try command line argument */
- int i = find_arg_with_value(ua, _("client"));
+ int i = find_arg_with_value(ua, NT_("client"));
if (i >= 0) {
+ if (!has_value(ua, i)) {
+ return 0;
+ }
bstrncpy(rx->ClientName, ua->argv[i], sizeof(rx->ClientName));
return 1;
}
return 1;
}
+
/*
* The first step in the restore process is for the user to
* select a list of JobIds from which he will subsequently
bool done = false;
int i, j;
const char *list[] = {
- "List last 20 Jobs run",
- "List Jobs where a given File is saved",
- "Enter list of comma separated JobIds to select",
- "Enter SQL list command",
- "Select the most recent backup for a client",
- "Select backup for a client before a specified time",
- "Enter a list of files to restore",
- "Enter a list of files to restore before a specified time",
- "Find the JobIds of the most recent backup for a client",
- "Find the JobIds for a backup for a client before a specified time",
- "Enter a list of directories to restore for given JobIds",
- "Cancel",
+ _("List last 20 Jobs run"),
+ _("List Jobs where a given File is saved"),
+ _("Enter list of comma separated JobIds to select"),
+ _("Enter SQL list command"),
+ _("Select the most recent backup for a client"),
+ _("Select backup for a client before a specified time"),
+ _("Enter a list of files to restore"),
+ _("Enter a list of files to restore before a specified time"),
+ _("Find the JobIds of the most recent backup for a client"),
+ _("Find the JobIds for a backup for a client before a specified time"),
+ _("Enter a list of directories to restore for found JobIds"),
+ _("Cancel"),
NULL };
const char *kw[] = {
"fileset", /* 10 */
"where", /* 11 */
"yes", /* 12 */
- "done", /* 13 */
+ "bootstrap", /* 13 */
+ "done", /* 14 */
NULL
};
/* Found keyword in kw[] list, process it */
switch (j) {
case 0: /* jobid */
+ if (!has_value(ua, i)) {
+ return 0;
+ }
if (*rx->JobIds != 0) {
pm_strcat(rx->JobIds, ",");
}
have_date = true;
break;
case 2: /* before */
+ if (!has_value(ua, i)) {
+ return 0;
+ }
if (str_to_utime(ua->argv[i]) == 0) {
bsendmsg(ua, _("Improper date format: %s\n"), ua->argv[i]);
return 0;
break;
case 3: /* file */
case 4: /* dir */
+ if (!has_value(ua, i)) {
+ return 0;
+ }
if (!have_date) {
bstrutime(date, sizeof(date), time(NULL));
}
done = true;
break;
case 6: /* pool specified */
+ if (!has_value(ua, i)) {
+ return 0;
+ }
rx->pool = (POOL *)GetResWithName(R_POOL, ua->argv[i]);
if (!rx->pool) {
bsendmsg(ua, _("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]);
}
done = true;
switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) {
- case -1: /* error */
+ case -1: /* error or cancel */
return 0;
case 0: /* list last 20 Jobs run */
gui_save = ua->jcr->gui;
done = false;
break;
case 1: /* list where a file is saved */
+ if (!get_client_name(ua, rx)) {
+ return 0;
+ }
if (!get_cmd(ua, _("Enter Filename (no path):"))) {
return 0;
}
len = strlen(ua->cmd);
fname = (char *)malloc(len * 2 + 1);
db_escape_string(fname, ua->cmd, len);
- Mmsg(rx->query, uar_file, fname);
+ Mmsg(rx->query, uar_file, rx->ClientName, fname);
free(fname);
gui_save = ua->jcr->gui;
ua->jcr->gui = true;
if (!get_client_name(ua, rx)) {
return 0;
}
- bsendmsg(ua, _("Enter directory names with a trailing /, or < to enter a filename\n"
- "containg a list of directories and terminate\n"
- "them with a blank line.\n"));
+ bsendmsg(ua, _("Enter full directory names or start the name\n"
+ "with a < to indicate it is a filename containg a list\n"
+ "of directories and terminate them with a blank line.\n"));
for ( ;; ) {
if (!get_cmd(ua, _("Enter directory name: "))) {
return 0;
if (len == 0) {
break;
}
- if (ua->cmd[len-1] != '/') {
+ /* Add trailing slash to end of directory names */
+ if (ua->cmd[0] != '<' && ua->cmd[len-1] != '/') {
strcat(ua->cmd, "/");
}
insert_one_file_or_dir(ua, rx, date, true);
bsendmsg(ua, _("No Jobs selected.\n"));
return 0;
}
- bsendmsg(ua, _("You have selected the following JobId%s: %s\n"),
- strchr(rx->JobIds,',')?"s":"",rx->JobIds);
+ if (strchr(rx->JobIds,',')) {
+ bsendmsg(ua, _("You have selected the following JobIds: %s\n"), rx->JobIds);
+ }
+ else {
+ bsendmsg(ua, _("You have selected the following JobId: %s\n"), rx->JobIds);
+ }
- memset(&jr, 0, sizeof(JOB_DBR));
rx->TotalFiles = 0;
for (p=rx->JobIds; ; ) {
if (jr.JobId == JobId) {
continue; /* duplicate of last JobId */
}
+ memset(&jr, 0, sizeof(JOB_DBR));
jr.JobId = JobId;
if (!db_get_job_record(ua->jcr, ua->db, &jr)) {
char ed1[50];
}
fclose(ffd);
break;
+ case '?':
+ p++;
+ insert_table_into_findex_list(ua, rx, p);
+ break;
default:
if (dir) {
insert_dir_into_findex_list(ua, rx, ua->cmd, date);
return true;
}
+/*
+ * Get the JobId and FileIndexes of all files in the specified table
+ */
+static bool insert_table_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *table)
+{
+ char ed1[50];
+
+ strip_trailing_junk(table);
+ Mmsg(rx->query, uar_jobid_fileindex_from_table, table);
+
+ rx->found = false;
+ /* Find and insert jobid and File Index */
+ if (!db_sql_query(ua->db, rx->query, jobid_fileindex_handler, (void *)rx)) {
+ bsendmsg(ua, _("Query failed: %s. ERR=%s\n"),
+ rx->query, db_strerror(ua->db));
+ }
+ if (!rx->found) {
+ bsendmsg(ua, _("No table found: %s\n"), table);
+ return true;
+ }
+ /*
+ * Find the MediaTypes for this JobId and add to the name_list
+ */
+ Mmsg(rx->query, uar_mediatype, edit_int64(rx->JobId, ed1));
+ if (!db_sql_query(ua->db, rx->query, unique_name_list_handler, (void *)&rx->name_list)) {
+ bsendmsg(ua, "%s", db_strerror(ua->db));
+ return false;
+ }
+ return true;
+}
static void split_path_and_filename(RESTORE_CTX *rx, char *name)
{
}
}
if (tree.FileCount == 0) {
- bsendmsg(ua, "\nThere were no files inserted into the tree, so file selection\n"
- "is not possible.\nMost likely your retention policy pruned the files\n");
- if (!get_yesno(ua, _("Do you want to restore all the files? (yes|no): "))) {
+ bsendmsg(ua, _("\nThere were no files inserted into the tree, so file selection\n"
+ "is not possible.Most likely your retention policy pruned the files\n"));
+ if (!get_yesno(ua, _("\nDo you want to restore all the files? (yes|no): "))) {
OK = false;
} else {
last_JobId = 0;
}
} else {
char ec1[50];
- bsendmsg(ua, "\n%d Job%s, %s files inserted into the tree%s.\n",
- items, items==1?"":"s", edit_uint64_with_commas(tree.FileCount, ec1),
- tree.all?" and marked for extraction":"");
+ if (items==1) {
+ if (tree.all) {
+ bsendmsg(ua, _("\n1 Job, %s files inserted into the tree and marked for extraction.\n"),
+ edit_uint64_with_commas(tree.FileCount, ec1));
+ }
+ else {
+ bsendmsg(ua, _("\n1 Job, %s files inserted into the tree.\n"),
+ edit_uint64_with_commas(tree.FileCount, ec1));
+ }
+ }
+ else {
+ if (tree.all) {
+ bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree and marked for extraction.\n"),
+ items, edit_uint64_with_commas(tree.FileCount, ec1));
+ }
+ else {
+ bsendmsg(ua, _("\n%d Jobs, %s files inserted into the tree.\n"),
+ items, edit_uint64_with_commas(tree.FileCount, ec1));
+ }
+ }
/* Check MediaType and select storage that corresponds */
get_storage_from_mediatype(ua, &rx->name_list, rx);
- if (find_arg(ua, _("done")) < 0) {
+ if (find_arg(ua, NT_("done")) < 0) {
/* Let the user interact in selecting which files to restore */
OK = user_select_files_from_tree(&tree);
}
}
-/* Return next JobId from comma separated list */
-static int get_next_jobid_from_list(char **p, uint32_t *JobId)
+/*
+ * Return next JobId from comma separated list
+ *
+ * Returns:
+ * 1 if next JobId returned
+ * 0 if no more JobIds are in list
+ * -1 there is an error
+ */
+int get_next_jobid_from_list(char **p, JobId_t *JobId)
{
char jobid[30];
char *q = *p;