X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fsrc%2Fdird%2Fua_restore.c;h=a625da14c9d40e076111b73fff87e5afe53958a7;hb=3f8a3a045ea058657030f588a10f786449d00e0d;hp=25438c3e1a00db20ba3092512cd78e7a9199fde5;hpb=dd1d83a678b1ffe011af1e1c7bcdffc82c6a1dce;p=bacula%2Fbacula diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index 25438c3e1a..a625da14c9 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -12,27 +12,22 @@ * * 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" @@ -49,50 +44,13 @@ extern char *uar_sel_all_temp1, *uar_sel_fileset, *uar_mediatype; 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[20]; - 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); @@ -112,6 +70,7 @@ static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, b 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 @@ -122,6 +81,7 @@ int restore_cmd(UAContext *ua, const char *cmd) 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); @@ -177,16 +137,25 @@ int restore_cmd(UAContext *ua, const char *cmd) } 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; @@ -210,21 +179,21 @@ int restore_cmd(UAContext *ua, const char *cmd) /* 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); @@ -259,14 +228,26 @@ static void free_rx(RESTORE_CTX *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; } @@ -279,6 +260,7 @@ static int get_client_name(UAContext *ua, RESTORE_CTX *rx) return 1; } + /* * The first step in the restore process is for the user to * select a list of JobIds from which he will subsequently @@ -298,18 +280,18 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) 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[] = { @@ -329,7 +311,8 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) "fileset", /* 10 */ "where", /* 11 */ "yes", /* 12 */ - "done", /* 13 */ + "bootstrap", /* 13 */ + "done", /* 14 */ NULL }; @@ -350,6 +333,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) /* 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, ","); } @@ -361,6 +347,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) 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; @@ -370,6 +359,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) break; case 3: /* file */ case 4: /* dir */ + if (!has_value(ua, i)) { + return 0; + } if (!have_date) { bstrutime(date, sizeof(date), time(NULL)); } @@ -394,6 +386,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) 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]); @@ -438,7 +433,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) } 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; @@ -501,7 +496,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) return 0; } bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n" - "containg a list of file names with paths, and terminate\n" + "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { @@ -526,7 +521,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) return 0; } bsendmsg(ua, _("Enter file names with paths, or < to enter a filename\n" - "containg a list of file names with paths, and terminate\n" + "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { @@ -579,9 +574,9 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) 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 containing a list\n" + "of directories and terminate them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter directory name: "))) { return 0; @@ -590,7 +585,8 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) 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); @@ -610,10 +606,13 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) 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; ; ) { @@ -628,6 +627,7 @@ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) 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]; @@ -698,6 +698,10 @@ static void insert_one_file_or_dir(UAContext *ua, RESTORE_CTX *rx, char *date, b } 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); @@ -718,7 +722,7 @@ static bool insert_file_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *f { char ed1[50]; - strip_trailing_junk(file); + strip_trailing_newline(file); split_path_and_filename(rx, file); if (*rx->JobIds == 0) { Mmsg(rx->query, uar_jobid_fileindex, date, rx->path, rx->fname, @@ -786,6 +790,36 @@ static bool insert_dir_into_findex_list(UAContext *ua, RESTORE_CTX *rx, char *di 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) { @@ -897,8 +931,8 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx) } } if (tree.FileCount == 0) { - 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"); + 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 { @@ -913,14 +947,31 @@ static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx) } } 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); } @@ -1099,8 +1150,15 @@ bail_out: } -/* 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;