From 5483b82f16f28424ca126db50f7bf71e53023e3e Mon Sep 17 00:00:00 2001 From: Kern Sibbald Date: Sun, 4 Aug 2002 17:12:56 +0000 Subject: [PATCH] restore cmd + misc -- see kes04Aug02 git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@75 91ce42f0-d328-0410-95d8-f526ca767f89 --- bacula/ChangeLog | 2 +- bacula/kernstodo | 5 +- bacula/src/baconfig.h | 4 +- bacula/src/cats/protos.h | 4 +- bacula/src/cats/sql.c | 37 ++++ bacula/src/cats/sql_create.c | 7 - bacula/src/cats/sql_update.c | 9 - bacula/src/console/console.c | 2 +- bacula/src/dird/catreq.c | 1 + bacula/src/dird/fd_cmds.c | 4 +- bacula/src/dird/getmsg.c | 1 - bacula/src/dird/job.c | 1 + bacula/src/dird/msgchan.c | 1 + bacula/src/dird/restore.c | 2 +- bacula/src/dird/ua_input.c | 48 +++-- bacula/src/dird/ua_prune.c | 8 +- bacula/src/dird/ua_restore.c | 358 +++++++++++++++++++++++++++-------- bacula/src/dird/ua_server.c | 8 +- bacula/src/filed/backup.c | 2 +- bacula/src/filed/job.c | 16 +- bacula/src/filed/status.c | 2 +- bacula/src/lib/bnet.c | 18 +- bacula/src/lib/bsock.h | 1 - bacula/src/lib/message.c | 4 +- bacula/src/lib/protos.h | 1 + bacula/src/lib/util.c | 2 + bacula/src/stored/dircmd.c | 10 +- bacula/src/stored/fd_cmds.c | 6 +- bacula/src/stored/read.c | 2 +- bacula/src/version.h | 4 +- 30 files changed, 402 insertions(+), 168 deletions(-) diff --git a/bacula/ChangeLog b/bacula/ChangeLog index ebfe6c0406..98adef6fff 100644 --- a/bacula/ChangeLog +++ b/bacula/ChangeLog @@ -1,6 +1,6 @@ 2002-07-26 Release 1.23a - Fix segmentation fault is FD status. -- Turn off TRANACTIONs for SQLite. +- Turn off TRANSACTIONs for SQLite. 2002-07-23 Release 1.23 From kes22Jul02 diff --git a/bacula/kernstodo b/bacula/kernstodo index 65ada9928f..e00fe785af 100644 --- a/bacula/kernstodo +++ b/bacula/kernstodo @@ -1,6 +1,9 @@ Kern's ToDo List - 25 July 2002 + 3 August 2002 +Irix conversion notes: +- no uuencode +- no hostname To do: - Document passwords. - Document running multiple Jobs diff --git a/bacula/src/baconfig.h b/bacula/src/baconfig.h index 0b9213125a..dfd21d6b49 100644 --- a/bacula/src/baconfig.h +++ b/bacula/src/baconfig.h @@ -75,7 +75,9 @@ #define TAPE_BSIZE 1024 #if !defined(DEV_BSIZE) && defined(BSIZE) #define DEV_BSIZE BSIZE -#else +#endif + +#ifndef DEV_BSIZE #define DEV_BSIZE 512 #endif diff --git a/bacula/src/cats/protos.h b/bacula/src/cats/protos.h index c3ca48c5a3..a38e5ce936 100644 --- a/bacula/src/cats/protos.h +++ b/bacula/src/cats/protos.h @@ -41,6 +41,8 @@ int db_sql_query(B_DB *mdb, char *cmd, DB_RESULT_HANDLER *result_handler, void * int check_tables_version(B_DB *mdb); void _db_unlock(char *file, int line, B_DB *mdb); void _db_lock(char *file, int line, B_DB *mdb); +void db_start_transaction(B_DB *mdb); +void db_end_transaction(B_DB *mdb); /* create.c */ int db_create_file_attributes_record(B_DB *mdb, ATTR_DBR *ar); @@ -88,7 +90,7 @@ int db_update_job_start_record(B_DB *db, JOB_DBR *jr); int db_update_job_end_record(B_DB *db, JOB_DBR *jr); int db_update_pool_record(B_DB *db, POOL_DBR *pr); int db_update_media_record(B_DB *db, MEDIA_DBR *mr); -int db_add_MD5_to_file_record(B_DB *mdb, FileId_t FileId, char *MD5); +int db_add_MD5_to_file_record(B_DB *mdb, FileId_t FileId, char *MD5); int db_mark_file_record(B_DB *mdb, FileId_t FileId, JobId_t JobId); #endif /* __SQL_PROTOS_H */ diff --git a/bacula/src/cats/sql.c b/bacula/src/cats/sql.c index ece888e7d6..c16f59e6cc 100644 --- a/bacula/src/cats/sql.c +++ b/bacula/src/cats/sql.c @@ -219,5 +219,42 @@ void _db_unlock(char *file, int line, B_DB *mdb) } } +/* + * Start a transaction. This groups inserts and makes things + * much more efficient. Usually started when inserting + * file attributes. + */ +void db_start_transaction(B_DB *mdb) +{ +#ifdef HAVE_SQLITE + db_lock(mdb); + /* Allow only 10,000 changes per transaction */ + if (mdb->transaction && mdb->changes > 10000) { + db_end_transaction(mdb); + } + if (!mdb->transaction) { + my_sqlite_query(mdb, "BEGIN"); /* begin transaction */ + Dmsg0(000, "Start SQLite transaction\n"); + mdb->transaction = 1; + } + db_unlock(mdb); +#endif + +} + +void db_end_transaction(B_DB *mdb) +{ +#ifdef HAVE_SQLITE + db_lock(mdb); + if (mdb->transaction) { + my_sqlite_query(mdb, "COMMIT"); /* end transaction */ + mdb->transaction = 0; + Dmsg1(000, "End SQLite transaction changes=%d\n", mdb->changes); + } + mdb->changes = 0; + db_unlock(mdb); +#endif +} + #endif /* HAVE_MYSQL | HAVE_SQLITE */ diff --git a/bacula/src/cats/sql_create.c b/bacula/src/cats/sql_create.c index e5502af608..7674ec14a3 100644 --- a/bacula/src/cats/sql_create.c +++ b/bacula/src/cats/sql_create.c @@ -505,13 +505,6 @@ int db_create_file_attributes_record(B_DB *mdb, ATTR_DBR *ar) Dmsg0(50, "db_create_file_record\n"); Dmsg3(100, "Path=%s File=%s FilenameId=%d\n", spath, file, ar->FilenameId); -#ifdef HAVE_SQLITE - if (mdb->transaction && mdb->changes > 10000) { - my_sqlite_query(mdb, "COMMIT"); /* end transaction */ - my_sqlite_query(mdb, "BEGIN"); /* start new transaction */ - mdb->changes = 0; - } -#endif return 1; } diff --git a/bacula/src/cats/sql_update.c b/bacula/src/cats/sql_update.c index 1d778acbc3..834e6e017f 100644 --- a/bacula/src/cats/sql_update.c +++ b/bacula/src/cats/sql_update.c @@ -107,11 +107,6 @@ ClientId=%d, JobTDate=%s WHERE JobId=%d", (char)(jr->Level), dt, jr->ClientId, edit_uint64(JobTDate, ed1), jr->JobId); stat = UPDATE_DB(mdb, mdb->cmd); db_unlock(mdb); -#ifdef HAVE_SQLITE - /******FIXME***** do this machine independently */ -// my_sqlite_query(mdb, "BEGIN"); /* begin transaction */ -// mdb->transaction = 1; -#endif mdb->changes = 0; return stat; } @@ -149,10 +144,6 @@ VolSessionTime=%d, PoolId=%d, FileSetId=%d, JobTDate=%s WHERE JobId=%d", jr->PoolId, jr->FileSetId, edit_uint64(JobTDate, ed2), jr->JobId); stat = UPDATE_DB(mdb, mdb->cmd); -#ifdef HAVE_SQLITE - my_sqlite_query(mdb, "COMMIT"); /* end transaction */ - mdb->transaction = 0; -#endif db_unlock(mdb); return stat; } diff --git a/bacula/src/console/console.c b/bacula/src/console/console.c index fe0aac5b2f..616cbaf980 100644 --- a/bacula/src/console/console.c +++ b/bacula/src/console/console.c @@ -130,7 +130,7 @@ static void read_and_process_input(FILE *input, BSOCK *UA_sock) break; /* error */ } } - if (strcmp(UA_sock->msg, "quit") == 0 || strcmp(UA_sock->msg, "exit") == 0) { + if (strcmp(UA_sock->msg, ".quit") == 0 || strcmp(UA_sock->msg, ".exit") == 0) { break; } while ((stat = bnet_recv(UA_sock)) > 0) { diff --git a/bacula/src/dird/catreq.c b/bacula/src/dird/catreq.c index 7d7b21b599..f9b7960f71 100644 --- a/bacula/src/dird/catreq.c +++ b/bacula/src/dird/catreq.c @@ -235,6 +235,7 @@ void catalog_update(JCR *jcr, BSOCK *bs, char *msg) if (!jcr->pool->catalog_files) { return; } + db_start_transaction(jcr->db); /* start transaction if not already open */ skip_nonspaces(&p); /* UpdCat */ skip_spaces(&p); skip_nonspaces(&p); /* Job=nnn */ diff --git a/bacula/src/dird/fd_cmds.c b/bacula/src/dird/fd_cmds.c index 85f990c2af..fb5f8a953e 100644 --- a/bacula/src/dird/fd_cmds.c +++ b/bacula/src/dird/fd_cmds.c @@ -131,7 +131,7 @@ int send_include_list(JCR *jcr) return 0; } } - bnet_sig(fd, BNET_EOF); + bnet_sig(fd, BNET_EOD); /* end of data */ fd->msg = msgsave; if (!response(fd, OKinc, "Include")) { jcr->JobStatus = JS_ErrorTerminated; @@ -166,7 +166,7 @@ int send_exclude_list(JCR *jcr) return 0; } } - bnet_sig(fd, BNET_EOF); + bnet_sig(fd, BNET_EOD); fd->msg = msgsave; if (!response(fd, OKexc, "Exclude")) { jcr->JobStatus = JS_ErrorTerminated; diff --git a/bacula/src/dird/getmsg.c b/bacula/src/dird/getmsg.c index ca6273cc02..7c1caf5118 100644 --- a/bacula/src/dird/getmsg.c +++ b/bacula/src/dird/getmsg.c @@ -80,7 +80,6 @@ int32_t bget_msg(BSOCK *bs, int rtn) if (n == 0) { /* handle signal */ /* 0 return from bnet_recv() => network signal */ switch (bs->msglen) { - case BNET_EOF: /* deprecated */ case BNET_EOD: /* end of data */ return 0; case BNET_EOD_POLL: diff --git a/bacula/src/dird/job.c b/bacula/src/dird/job.c index ec96726857..7c9678f60e 100644 --- a/bacula/src/dird/job.c +++ b/bacula/src/dird/job.c @@ -183,6 +183,7 @@ static void job_thread(void *arg) case JT_ADMIN: /* No actual job */ do_autoprune(jcr); + jcr->JobStatus = JS_Terminated; break; default: Dmsg1(0, "Unimplemented job type: %d\n", jcr->JobType); diff --git a/bacula/src/dird/msgchan.c b/bacula/src/dird/msgchan.c index 324e0d4389..82919650a9 100644 --- a/bacula/src/dird/msgchan.c +++ b/bacula/src/dird/msgchan.c @@ -183,6 +183,7 @@ static void msg_thread_cleanup(void *arg) { JCR *jcr = (JCR *)arg; Dmsg0(200, "End msg_thread\n"); + db_end_transaction(jcr->db); /* terminate any open transaction */ P(jcr->mutex); jcr->msg_thread_done = TRUE; pthread_cond_broadcast(&jcr->term_wait); /* wakeup any waiting threads */ diff --git a/bacula/src/dird/restore.c b/bacula/src/dird/restore.c index bd510d8e0e..5f3a49dea2 100644 --- a/bacula/src/dird/restore.c +++ b/bacula/src/dird/restore.c @@ -298,7 +298,7 @@ static int send_bootstrap_file(JCR *jcr) fd->msglen = Mmsg(&fd->msg, "%s", buf); bnet_send(fd); } - bnet_sig(fd, BNET_EOF); + bnet_sig(fd, BNET_EOD); fclose(bs); if (!response(fd, OKbootstrap, "Bootstrap")) { jcr->JobStatus = JS_ErrorTerminated; diff --git a/bacula/src/dird/ua_input.c b/bacula/src/dird/ua_input.c index 85d7feefdd..7aa6730160 100644 --- a/bacula/src/dird/ua_input.c +++ b/bacula/src/dird/ua_input.c @@ -52,8 +52,7 @@ int get_cmd(UAContext *ua, char *prompt) return 0; } ua->cmd = (char *) check_pool_memory_size(ua->cmd, sock->msglen+1); - strcpy(ua->cmd, sock->msg); - ua->cmd[sock->msglen] = 0; + bstrncpy(ua->cmd, sock->msg, sock->msglen+1); strip_trailing_junk(ua->cmd); if (strcmp(ua->cmd, ".messages") == 0) { qmessagescmd(ua, ua->cmd); @@ -74,31 +73,39 @@ int get_cmd(UAContext *ua, char *prompt) */ char *next_arg(char **s) { - char *p, *n; + char *p, *q, *n; + Dmsg1(400, "Next arg=%s\n", *s); /* skip past spaces to next arg */ - for (p=*s; *p && *p == ' '; p++) - {} + for (p=*s; *p && *p == ' '; ) { + p++; + } /* Determine start of argument */ if (*p == '"') { - n = p+1; /* skip leading quote */ + Dmsg0(400, "Start with quote.\n"); + for (n = q = ++p; *p && *p != '"'; ) { + if (*p == '\\') { + p++; + } + *q++ = *p++; + } + p++; /* skip terminating quote */ + for ( ; *p && *p != ' '; ) { + *q++ = *p++; + } + *q = 0; } else { + /* Scan argment and terminate it */ n = p; - } - /* Scan argment and terminate it */ - for ( ; *p && *p != ' '; p++) { - if (*p == '"') { - for (p++; *p && *p != '"'; p++) { - *(p-1) = *p; - *p = 0; - } - break; + for ( ; *p && *p != ' '; ) { + p++; + } + if (*p == ' ') { + *p++ = 0; } - } - if (*p) { /* if more arguments */ - *p++ = 0; /* terminate this one */ } *s = p; + Dmsg2(400, "End arg=%s next=%s\n", n, p); return n; } @@ -129,8 +136,7 @@ void parse_command_args(UAContext *ua) int i; ua->args = (char *) check_pool_memory_size(ua->args, sock->msglen+1); - strcpy(ua->args, sock->msg); - ua->args[sock->msglen] = 0; + bstrncpy(ua->args, sock->msg, sock->msglen+1); strip_trailing_junk(ua->args); ua->argc = 0; p = ua->args; @@ -154,7 +160,7 @@ void parse_command_args(UAContext *ua) } ua->argv[i] = p; /* save ptr to value or NULL */ } -#ifdef xxxxxxxxx +#ifdef xxxx for (i=0; iargc; i++) { Dmsg3(000, "Arg %d: kw=%s val=%s\n", i, ua->argk[i], ua->argv[i]?ua->argv[i]:"NULL"); diff --git a/bacula/src/dird/ua_prune.c b/bacula/src/dird/ua_prune.c index fd972682ee..0d7c2edf46 100644 --- a/bacula/src/dird/ua_prune.c +++ b/bacula/src/dird/ua_prune.c @@ -311,8 +311,8 @@ int prune_files(UAContext *ua, CLIENT *client) } edit_uint64_with_commas(del.tot_ids, ed1); edit_uint64_with_commas(del.num_ids, ed2); - bsendmsg(ua, _("Pruned %s Files from %s Jobs for client %s from %s catalog.\n"), - ed1, ed2, client->hdr.name, client->catalog->hdr.name); + bsendmsg(ua, _("Pruned %s Files from %s Jobs for client %s from catalog.\n"), + ed1, ed2, client->hdr.name); bail_out: db_unlock(ua->db); @@ -465,8 +465,8 @@ int prune_jobs(UAContext *ua, CLIENT *client, int JobType) db_sql_query(ua->db, query, NULL, (void *)NULL); Dmsg1(050, "Del sql=%s\n", query); } - bsendmsg(ua, _("Pruned %d %s for client %s from %s catalog.\n"), del.num_ids, - del.num_ids==1?_("Job"):_("Jobs"), client->hdr.name, client->catalog->hdr.name); + bsendmsg(ua, _("Pruned %d %s for client %s from catalog.\n"), del.num_ids, + del.num_ids==1?_("Job"):_("Jobs"), client->hdr.name); bail_out: drop_temp_tables(ua); diff --git a/bacula/src/dird/ua_restore.c b/bacula/src/dird/ua_restore.c index f45832be4f..85a70e963d 100644 --- a/bacula/src/dird/ua_restore.c +++ b/bacula/src/dird/ua_restore.c @@ -39,6 +39,15 @@ extern char *uar_list_jobs; extern char *uar_file; extern char *uar_sel_files; +extern char *uar_del_temp; +extern char *uar_del_temp1; +extern char *uar_create_temp; +extern char *uar_create_temp1; +extern char *uar_last_full; +extern char *uar_full; +extern char *uar_inc; +extern char *uar_list_temp; +extern char *uar_sel_jobid_temp; /* Context for insert_tree_handler() */ typedef struct s_tree_ctx { @@ -49,6 +58,13 @@ typedef struct s_tree_ctx { UAContext *ua; } TREE_CTX; +struct s_full_ctx { + btime_t JobTDate; + uint32_t ClientId; + uint32_t TotalFiles; + char JobIds[200]; +}; + /* FileIndex entry in bootstrap record */ typedef struct s_rbsr_findex { @@ -74,10 +90,13 @@ static void print_bsr(UAContext *ua, RBSR *bsr); static int complete_bsr(UAContext *ua, RBSR *bsr); static int insert_tree_handler(void *ctx, int num_fields, char **row); static void add_findex(RBSR *bsr, uint32_t JobId, int32_t findex); +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 next_jobid_from_list(char **p, uint32_t *JobId); +static int user_select_jobids(UAContext *ua, struct s_full_ctx *full); static void user_select_files(TREE_CTX *tree); - /* * Restore files * @@ -85,138 +104,273 @@ static void user_select_files(TREE_CTX *tree); int restorecmd(UAContext *ua, char *cmd) { POOLMEM *query; - int JobId, done = 0; TREE_CTX tree; + JobId_t JobId; + char *p; RBSR *bsr; char *nofname = ""; - JOB_DBR jr; - char *list[] = { - "List last Jobs run", - "Enter list of JobIds", - "Enter SQL list command", - "Select a File", - "Cancel", - NULL }; + struct s_full_ctx full; if (!open_db(ua)) { return 0; } memset(&tree, 0, sizeof(TREE_CTX)); + memset(&full, 0, sizeof(full)); + + if (!user_select_jobids(ua, &full)) { + return 0; + } + + + /* + * Build the directory tree + */ + tree.root = new_tree(full.TotalFiles); + tree.root->fname = nofname; + tree.ua = ua; + query = get_pool_memory(PM_MESSAGE); + for (p=full.JobIds; next_jobid_from_list(&p, &JobId) > 0; ) { + bsendmsg(ua, _("Building directory tree for JobId %u ...\n"), JobId); + Mmsg(&query, uar_sel_files, JobId); + if (!db_sql_query(ua->db, query, insert_tree_handler, (void *)&tree)) { + bsendmsg(ua, "%s", db_strerror(ua->db)); + } + } + bsendmsg(ua, "\n"); + free_pool_memory(query); + + /* Let the user select which files to restore */ + user_select_files(&tree); + + /* + * Walk down through the tree finding all files marked to be + * extracted making a bootstrap file. + */ + bsr = new_bsr(); + for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) { + Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node); + if (node->extract) { + Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex); + add_findex(bsr, node->JobId, node->FileIndex); + } + } + + free_tree(tree.root); /* free the directory tree */ + + if (bsr->JobId) { + complete_bsr(ua, bsr); /* find Vol, SessId, SessTime from JobIds */ + print_bsr(ua, bsr); + } else { + bsendmsg(ua, _("No files selected to restore.\n")); + } + free_bsr(bsr); + + bsendmsg(ua, _("Restore command done.\n")); + return 1; +} + +/* + * The first step in the restore process is for the user to + * select a list of JobIds from which he will subsequently + * select which files are to be restored. + */ +static int user_select_jobids(UAContext *ua, struct s_full_ctx *full) +{ + char *p; + JobId_t JobId; + JOB_DBR jr; + POOLMEM *query; + int done = 0; + char *list[] = { + "List last 20 Jobs run", + "List Jobs where a given File is saved", + "Enter list of JobIds to select", + "Enter SQL list command", + "Select the most recent backup for a client" + "Cancel", + NULL }; + + bsendmsg(ua, _("\nFirst you select one or more JobIds that contain files\n" + "to be restored. You will be presented several methods\n" + "of specifying the JobIds. Then you will be allowed to\n" + "select which files from those JobIds are to be restored.\n\n")); for ( ; !done; ) { - start_prompt(ua, _("To narrow down the restore, you have the following choices:\n")); + start_prompt(ua, _("To select the JobIds, you have the following choices:\n")); for (int i=0; list[i]; i++) { add_prompt(ua, list[i]); } done = 1; switch (do_prompt(ua, "Select item: ", NULL)) { - case -1: + case -1: /* error */ return 0; - case 0: + case 0: /* list last 20 Jobs run */ db_list_sql_query(ua->db, uar_list_jobs, prtit, ua, 1); - if (!get_cmd(ua, _("Enter JobId to select files for restore: "))) { + done = 0; + break; + case 1: /* list where a file is saved */ + if (!get_cmd(ua, _("Enter Filename: "))) { return 0; } - if (!is_a_number(ua->cmd)) { - bsendmsg(ua, _("Bad JobId entered.\n")); - return 0; - } - JobId = atoi(ua->cmd); + query = get_pool_memory(PM_MESSAGE); + Mmsg(&query, uar_file, ua->cmd); + db_list_sql_query(ua->db, query, prtit, ua, 1); + free_pool_memory(query); + done = 0; break; - - case 1: - if (!get_cmd(ua, _("Enter JobIds: "))) { + case 2: /* enter a list of JobIds */ + if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { return 0; } - JobId = atoi(ua->cmd); + bstrncpy(full->JobIds, ua->cmd, sizeof(full->JobIds)); break; - case 2: + case 3: /* Enter an SQL list command */ if (!get_cmd(ua, _("Enter SQL list command: "))) { return 0; } db_list_sql_query(ua->db, ua->cmd, prtit, ua, 1); done = 0; break; - case 3: - if (!get_cmd(ua, _("Enter Filename: "))) { + case 4: /* Select the most recent backups */ + db_sql_query(ua->db, uar_del_temp, NULL, NULL); + db_sql_query(ua->db, uar_del_temp1, NULL, NULL); + if (!db_sql_query(ua->db, uar_create_temp, NULL, NULL)) { + bsendmsg(ua, "%s\n", db_strerror(ua->db)); + } + if (!db_sql_query(ua->db, uar_create_temp1, NULL, NULL)) { + bsendmsg(ua, "%s\n", db_strerror(ua->db)); + } + if (!get_cmd(ua, _("Enter Client name: "))) { return 0; } query = get_pool_memory(PM_MESSAGE); - Mmsg(&query, uar_file, ua->cmd); - db_list_sql_query(ua->db, query, prtit, ua, 1); - free_pool_memory(query); - if (!get_cmd(ua, _("Enter JobId to select files for restore: "))) { - return 0; + Mmsg(&query, uar_last_full, ua->cmd); + /* Find JobId of full Backup of system */ + if (!db_sql_query(ua->db, query, NULL, NULL)) { + bsendmsg(ua, "%s\n", db_strerror(ua->db)); + } + /* Find all Volumes used by that JobId */ + if (!db_sql_query(ua->db, uar_full, NULL,NULL)) { + bsendmsg(ua, "%s\n", db_strerror(ua->db)); + } + /* Note, this is needed as I don't seem to get the callback + * from the call just above. + */ + if (!db_sql_query(ua->db, "SELECT * from temp1", last_full_handler, (void *)full)) { + bsendmsg(ua, "%s\n", db_strerror(ua->db)); } - if (!is_a_number(ua->cmd)) { - bsendmsg(ua, _("Bad JobId entered.\n")); - return 0; + /* Now find all Incremental Jobs */ + Mmsg(&query, uar_inc, (uint32_t)full->JobTDate, full->ClientId); + if (!db_sql_query(ua->db, query, NULL, NULL)) { + bsendmsg(ua, "%s\n", db_strerror(ua->db)); } - JobId = atoi(ua->cmd); + free_pool_memory(query); + db_list_sql_query(ua->db, uar_list_temp, prtit, ua, 1); + + if (!db_sql_query(ua->db, uar_sel_jobid_temp, jobid_handler, (void *)full)) { + bsendmsg(ua, "%s\n", db_strerror(ua->db)); + } + db_sql_query(ua->db, uar_del_temp, NULL, NULL); + db_sql_query(ua->db, uar_del_temp1, NULL, NULL); break; - case 4: + case 5: return 0; } } - memset(&jr, 0, sizeof(JOB_DBR)); - jr.JobId = JobId; - if (!db_get_job_record(ua->db, &jr)) { - bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db)); + if (*full->JobIds == 0) { + bsendmsg(ua, _("No Jobs selected.\n")); return 0; } + bsendmsg(ua, _("You have selected the following JobId: %s\n"), full->JobIds); - /* - * Build the directory tree - */ - bsendmsg(ua, _("Building directory tree of backed up files ...\n")); - memset(&tree, 0, sizeof(tree)); - tree.root = new_tree(jr.JobFiles); - tree.root->fname = nofname; - tree.ua = ua; - query = get_pool_memory(PM_MESSAGE); - Mmsg(&query, uar_sel_files, JobId); - if (!db_sql_query(ua->db, query, insert_tree_handler, (void *)&tree)) { - bsendmsg(ua, "%s", db_strerror(ua->db)); - } - free_pool_memory(query); + memset(&jr, 0, sizeof(JOB_DBR)); - /* Let the user select which files to restore */ - user_select_files(&tree); + for (p=full->JobIds; ; ) { + int stat = next_jobid_from_list(&p, &JobId); + if (stat < 0) { + bsendmsg(ua, _("Invalid JobId in list.\n")); + return 0; + } + if (stat == 0) { + break; + } + jr.JobId = JobId; + if (!db_get_job_record(ua->db, &jr)) { + bsendmsg(ua, _("Unable to get Job record. ERR=%s\n"), db_strerror(ua->db)); + return 0; + } + full->TotalFiles = jr.JobFiles; + } + return 1; +} - /* - * Walk down through the tree finding all files marked to be - * extracted making a bootstrap file. - */ - bsr = new_bsr(); - for (TREE_NODE *node=first_tree_node(tree.root); node; node=next_tree_node(node)) { - Dmsg2(400, "FI=%d node=0x%x\n", node->FileIndex, node); - if (node->extract) { - Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex); - add_findex(bsr, node->JobId, node->FileIndex); +static int next_jobid_from_list(char **p, uint32_t *JobId) +{ + char jobid[30]; + int i; + char *q = *p; + + jobid[0] = 0; + for (i=0; i<(int)sizeof(jobid); i++) { + if (*q == ',') { + q++; + break; } + jobid[i] = *q++; + jobid[i+1] = 0; } + if (jobid[0] == 0 || !is_a_number(jobid)) { + return 0; + } + *p = q; + *JobId = strtoul(jobid, NULL, 10); + if (errno) { + return 0; + } + return 1; +} - free_tree(tree.root); /* free the directory tree */ +/* + * Callback handler make list of JobIds + */ +static int jobid_handler(void *ctx, int num_fields, char **row) +{ + struct s_full_ctx *full = (struct s_full_ctx *)ctx; - if (bsr->JobId) { - complete_bsr(ua, bsr); - print_bsr(ua, bsr); - } else { - bsendmsg(ua, _("No files selected to restore.\n")); + if (strlen(full->JobIds)+strlen(row[0])+2 < sizeof(full->JobIds)) { + if (full->JobIds[0] != 0) { + strcat(full->JobIds, ","); + } + strcat(full->JobIds, row[0]); } - free_bsr(bsr); - bsendmsg(ua, _("Restore command done.\n")); - return 1; + return 0; +} + + +/* + * Callback handler to pickup last Full backup JobId and ClientId + */ +static int last_full_handler(void *ctx, int num_fields, char **row) +{ + struct s_full_ctx *full = (struct s_full_ctx *)ctx; + + full->JobTDate = atoi(row[1]); + full->ClientId = atoi(row[2]); + + return 0; } + /* Forward referenced commands */ static int addcmd(UAContext *ua, TREE_CTX *tree); +static int countcmd(UAContext *ua, TREE_CTX *tree); +static int findcmd(UAContext *ua, TREE_CTX *tree); static int lscmd(UAContext *ua, TREE_CTX *tree); static int helpcmd(UAContext *ua, TREE_CTX *tree); static int cdcmd(UAContext *ua, TREE_CTX *tree); @@ -228,6 +382,8 @@ static int quitcmd(UAContext *ua, TREE_CTX *tree); struct cmdstruct { char *key; int (*func)(UAContext *ua, TREE_CTX *tree); char *help; }; static struct cmdstruct commands[] = { { N_("add"), addcmd, _("add file")}, + { N_("count"), countcmd, _("count files")}, + { N_("find"), findcmd, _("find files")}, { N_("ls"), lscmd, _("list current directory")}, { N_("dir"), lscmd, _("list current directory")}, { N_("help"), helpcmd, _("print help")}, @@ -276,7 +432,7 @@ static void user_select_files(TREE_CTX *tree) break; } if (!found) { - bsendmsg(tree->ua, _("Illegal command\n")); + bsendmsg(tree->ua, _("Illegal command. Enter \"done\" to end.\n")); } if (!stat) { break; @@ -500,9 +656,11 @@ static int insert_tree_handler(void *ctx, int num_fields, char **row) new_node->FileIndex = atoi(row[2]); new_node->JobId = atoi(row[3]); new_node->type = type; +#ifdef xxxxxxx if (((tree->cnt) % 10000) == 0) { bsendmsg(tree->ua, "%d ", tree->cnt); } +#endif tree->cnt++; return 0; } @@ -541,6 +699,45 @@ static int addcmd(UAContext *ua, TREE_CTX *tree) return 1; } +static int countcmd(UAContext *ua, TREE_CTX *tree) +{ + int total, extract; + + total = extract = 0; + for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { + if (node->type != TN_NEWDIR) { + total++; + if (node->extract) { + extract++; + } + } + } + bsendmsg(ua, "%d total files. %d marked for extraction.\n", total, extract); + return 1; +} + +static int findcmd(UAContext *ua, TREE_CTX *tree) +{ + char cwd[2000]; + + if (ua->argc == 1) { + bsendmsg(ua, _("No file specification given.\n")); + return 0; + } + + for (int i=1; i < ua->argc; i++) { + for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) { + if (fnmatch(ua->argk[i], node->fname, 0) == 0) { + tree_getpath(node, cwd, sizeof(cwd)); + bsendmsg(ua, "%s\n", cwd); + } + } + } + return 1; +} + + + static int lscmd(UAContext *ua, TREE_CTX *tree) { TREE_NODE *node; @@ -572,11 +769,18 @@ static int helpcmd(UAContext *ua, TREE_CTX *tree) static int cdcmd(UAContext *ua, TREE_CTX *tree) { + TREE_NODE *node; char cwd[2000]; + if (ua->argc != 2) { return 1; } - tree->node = tree_cwd(ua->argk[1], tree->root, tree->node); + node = tree_cwd(ua->argk[1], tree->root, tree->node); + if (!node) { + bsendmsg(ua, _("Invalid path given.\n")); + } else { + tree->node = node; + } tree_getpath(tree->node, cwd, sizeof(cwd)); bsendmsg(ua, _("cwd is: %s\n"), cwd); return 1; diff --git a/bacula/src/dird/ua_server.c b/bacula/src/dird/ua_server.c index 861b92024d..6884ae4ff5 100644 --- a/bacula/src/dird/ua_server.c +++ b/bacula/src/dird/ua_server.c @@ -117,10 +117,8 @@ static void handle_UA_client_request(void *arg) while (!ua.quit) { stat = bnet_recv(ua.UA_sock); - Dmsg1(500, "stat=%d\n", stat); if (stat > 0) { - strncpy(cmd, ua.UA_sock->msg, sizeof(cmd)); - cmd[sizeof(cmd)-1] = 0; /* ensure it is terminated/trucated */ + bstrncpy(cmd, ua.UA_sock->msg, sizeof(cmd)); parse_command_args(&ua); if (ua.argc > 0 && ua.argk[0][0] == '.') { do_a_dot_command(&ua, cmd); @@ -139,12 +137,10 @@ static void handle_UA_client_request(void *arg) bnet_sig(ua.UA_sock, BNET_EOD); /* send end of command */ } } else if (stat == 0) { - if (ua.UA_sock->msglen == BNET_TERMINATE || - ua.UA_sock->msglen == BNET_EOF) { + if (ua.UA_sock->msglen == BNET_TERMINATE) { ua.quit = TRUE; break; } - Dmsg1(000, "stat=0 msglen=%d\n", ua.UA_sock->msglen); bnet_sig(ua.UA_sock, BNET_POLL); } else { break; /* error, exit */ diff --git a/bacula/src/filed/backup.c b/bacula/src/filed/backup.c index a1cdd6e59f..a9df1b575c 100644 --- a/bacula/src/filed/backup.c +++ b/bacula/src/filed/backup.c @@ -71,7 +71,7 @@ int blast_data_to_storage_daemon(JCR *jcr, char *addr, int port) stat = 0; /* error */ } - bnet_sig(sd, BNET_EOF); /* terminate data connection */ + bnet_sig(sd, BNET_EOD); /* end data connection */ if (jcr->big_buf) { free(jcr->big_buf); diff --git a/bacula/src/filed/job.c b/bacula/src/filed/job.c index 7e1fd7e014..a4c1474da9 100644 --- a/bacula/src/filed/job.c +++ b/bacula/src/filed/job.c @@ -231,7 +231,7 @@ static int cancel_cmd(JCR *jcr) } else { bnet_fsend(dir, "2902 Error scanning cancel command.\n"); } - bnet_sig(dir, BNET_EOF); + bnet_sig(dir, BNET_EOD); return 1; } @@ -577,11 +577,11 @@ cleanup: /* Inform Storage daemon that we are done */ if (sd) { - bnet_sig(sd, BNET_EOF); + bnet_sig(sd, BNET_TERMINATE); } /* Inform Director that we are done */ - bnet_sig(dir, BNET_EOF); + bnet_sig(dir, BNET_TERMINATE); return jcr->JobStatus == JS_Terminated; } @@ -637,7 +637,7 @@ static int verify_cmd(JCR *jcr) bnet_recv(sd); /* get OK */ /* Inform Storage daemon that we are done */ - bnet_sig(sd, BNET_EOF); + bnet_sig(sd, BNET_TERMINATE); break; default: @@ -646,7 +646,7 @@ static int verify_cmd(JCR *jcr) } /* Inform Director that we are done */ - return bnet_sig(dir, BNET_EOF); + return bnet_sig(dir, BNET_TERMINATE); } /* @@ -695,10 +695,10 @@ static int restore_cmd(JCR *jcr) bnet_recv(sd); /* get OK */ /* Inform Storage daemon that we are done */ - bnet_sig(sd, BNET_EOF); + bnet_sig(sd, BNET_TERMINATE); /* Inform Director that we are done */ - bnet_sig(dir, BNET_EOF); + bnet_sig(dir, BNET_TERMINATE); Dmsg0(30, "Done in job.c\n"); return 1; @@ -836,7 +836,7 @@ static int send_bootstrap_file(JCR *jcr) sd->msglen = Mmsg(&sd->msg, "%s", buf); bnet_send(sd); } - bnet_sig(sd, BNET_EOF); + bnet_sig(sd, BNET_EOD); fclose(bs); if (!response(sd, OKSDbootstrap, "Bootstrap")) { jcr->JobStatus = JS_ErrorTerminated; diff --git a/bacula/src/filed/status.c b/bacula/src/filed/status.c index 33c7cbcb9c..8cf036efb2 100755 --- a/bacula/src/filed/status.c +++ b/bacula/src/filed/status.c @@ -142,7 +142,7 @@ int status_cmd(JCR *jcr) do_status(sendit, (void *)user); bnet_fsend(user, "====\n"); - bnet_sig(user, BNET_EOF); + bnet_sig(user, BNET_EOD); return 1; } diff --git a/bacula/src/lib/bnet.c b/bacula/src/lib/bnet.c index 05c5a71430..1bf9863c94 100644 --- a/bacula/src/lib/bnet.c +++ b/bacula/src/lib/bnet.c @@ -116,7 +116,7 @@ bnet_recv(BSOCK *bsock) int32_t nbytes; int32_t pktsiz; - if (bsock->errors) { + if (bsock->errors || bsock->terminated) { return -2; } @@ -146,13 +146,11 @@ bnet_recv(BSOCK *bsock) pktsiz = ntohl(pktsiz); /* decode no. of bytes that follow */ - if (pktsiz <= 0) { - bsock->b_errno = ENODATA; - bsock->msglen = pktsiz; /* return size */ - return 0; /* soft EOF */ - } - /* For big packet size, something went wrong */ - if (pktsiz > 10000000) { + /* If signal or packet size too big */ + if (pktsiz <= 0 || pktsiz > 10000000) { + if (pktsiz == BNET_TERMINATE) { + bsock->terminated = 1; + } bsock->b_errno = ENODATA; bsock->msglen = pktsiz; /* return size */ return 0; /* soft EOF */ @@ -239,7 +237,7 @@ bnet_send(BSOCK *bsock) int32_t rc; int32_t pktsiz; - if (bsock->errors) { + if (bsock->errors || bsock->terminated) { return 0; } pktsiz = htonl((int32_t)bsock->msglen); @@ -579,8 +577,6 @@ char *bnet_sig_to_ascii(BSOCK *bs) { static char buf[30]; switch (bs->msglen) { - case BNET_EOF: - return "BNET_EOF"; case BNET_EOD: return "BNET_EOD"; case BNET_EOD_POLL: diff --git a/bacula/src/lib/bsock.h b/bacula/src/lib/bsock.h index a0ddf0a413..a507ad10bc 100644 --- a/bacula/src/lib/bsock.h +++ b/bacula/src/lib/bsock.h @@ -56,7 +56,6 @@ typedef struct s_bsock { } BSOCK; /* Signal definitions for use in bnet_sig() */ -#define BNET_EOF 0 /* Deprecated, use BNET_EOD */ #define BNET_EOD -1 /* End of data stream, new data may follow */ #define BNET_EOD_POLL -2 /* End of data and poll all in one */ #define BNET_STATUS -3 /* Send full status */ diff --git a/bacula/src/lib/message.c b/bacula/src/lib/message.c index d288dcc617..cd9abfcbb7 100755 --- a/bacula/src/lib/message.c +++ b/bacula/src/lib/message.c @@ -789,7 +789,7 @@ e_msg(char *file, int line, int type, int level, char *fmt,...) my_name, file, line); break; case M_ERROR_TERM: - sprintf(buf, "%s ERROR TERMINATING at %s:%d\n", + sprintf(buf, "%s ERROR TERMINATION at %s:%d\n", my_name, file, line); break; case M_FATAL: @@ -874,7 +874,7 @@ Jmsg(void *vjcr, int type, int level, char *fmt,...) sprintf(buf, "%s ABORTING due to ERROR\n", my_name); break; case M_ERROR_TERM: - sprintf(buf, "%s ERROR TERMINATING\n", my_name); + sprintf(buf, "%s ERROR TERMINATION\n", my_name); break; case M_FATAL: sprintf(buf, "%s: Job %s Fatal error: ", my_name, job); diff --git a/bacula/src/lib/protos.h b/bacula/src/lib/protos.h index 878469953d..20f5f3095e 100644 --- a/bacula/src/lib/protos.h +++ b/bacula/src/lib/protos.h @@ -32,6 +32,7 @@ void decode_stat (char *buf, struct stat *statp); int bin_to_base64 (char *buf, char *bin, int len); /* bmisc.c */ +char *bstrncpy (char *dest, char *src, int maxlen); void *b_malloc (char *file, int line, size_t size); #ifndef DEBUG void *bmalloc (size_t size); diff --git a/bacula/src/lib/util.c b/bacula/src/lib/util.c index 5bf396db94..2611cda0b6 100644 --- a/bacula/src/lib/util.c +++ b/bacula/src/lib/util.c @@ -425,6 +425,8 @@ char *job_type_to_str(int type) case JT_RESTORE: str = "Restore"; break; + case JT_ADMIN: + str = "Admin"; default: str = "Unknown Job Type"; break; diff --git a/bacula/src/stored/dircmd.c b/bacula/src/stored/dircmd.c index 95414cc1a9..1f5b4c8c5b 100644 --- a/bacula/src/stored/dircmd.c +++ b/bacula/src/stored/dircmd.c @@ -216,7 +216,7 @@ static int cancel_cmd(JCR *cjcr) } else { bnet_fsend(dir, _("3993 Error scanning cancel command.\n")); } - bnet_sig(dir, BNET_EOF); + bnet_sig(dir, BNET_EOD); return 1; } @@ -292,7 +292,7 @@ static int label_cmd(JCR *jcr) free_memory(volname); free_memory(poolname); free_memory(mtype); - bnet_sig(dir, BNET_EOF); + bnet_sig(dir, BNET_EOD); return 1; } @@ -503,7 +503,7 @@ static int mount_cmd(JCR *jcr) bnet_fsend(dir, _("3906 Error scanning mount command: %s\n"), dev_name); } free_memory(dev_name); - bnet_sig(dir, BNET_EOF); + bnet_sig(dir, BNET_EOD); return 1; } @@ -588,7 +588,7 @@ static int unmount_cmd(JCR *jcr) bnet_fsend(dir, _("3907 Error scanning unmount command: %s\n"), dname); } free_memory(dname); - bnet_sig(dir, BNET_EOF); + bnet_sig(dir, BNET_EOD); return 1; } @@ -720,6 +720,6 @@ static int status_cmd(JCR *jcr) #endif bnet_fsend(user, "====\n"); - bnet_sig(user, BNET_EOF); + bnet_sig(user, BNET_EOD); return 1; } diff --git a/bacula/src/stored/fd_cmds.c b/bacula/src/stored/fd_cmds.c index e770c719a6..90cec7925c 100644 --- a/bacula/src/stored/fd_cmds.c +++ b/bacula/src/stored/fd_cmds.c @@ -148,7 +148,7 @@ void run_job(JCR *jcr) bnet_fsend(dir, Job_end, jcr->Job, jcr->JobStatus, jcr->JobFiles, edit_uint64(jcr->JobBytes, ec1)); - bnet_sig(dir, BNET_EOF); /* send EOF to Director daemon */ + bnet_sig(dir, BNET_EOD); /* send EOD to Director daemon */ return; } @@ -234,7 +234,7 @@ static int append_close_session(JCR *jcr) bnet_fsend(fd, OK_close, jcr->NumVolumes); Dmsg1(60, ">filed: %s\n", fd->msg); - bnet_sig(fd, BNET_EOF); /* send EOF to File daemon */ + bnet_sig(fd, BNET_EOD); /* send EOD to File daemon */ Dmsg1(10, "Append close session: %s\n", dev_name(jcr->device->dev)); @@ -369,7 +369,7 @@ static int read_close_session(JCR *jcr) bnet_fsend(fd, OK_close); Dmsg1(60, ">filed: %s\n", fd->msg); - bnet_sig(fd, BNET_EOF); /* send EOF to File daemon */ + bnet_sig(fd, BNET_EOD); /* send EOD to File daemon */ jcr->session_opened = FALSE; return 1; diff --git a/bacula/src/stored/read.c b/bacula/src/stored/read.c index f298a53322..b39e23da15 100644 --- a/bacula/src/stored/read.c +++ b/bacula/src/stored/read.c @@ -237,7 +237,7 @@ int do_read_data(JCR *jcr) } } /* Send end of data to FD */ - bnet_sig(ds, BNET_EOF); + bnet_sig(ds, BNET_EOD); if (!release_device(jcr, dev, block)) { ok = FALSE; diff --git a/bacula/src/version.h b/bacula/src/version.h index 47140df20f..0916ff0969 100644 --- a/bacula/src/version.h +++ b/bacula/src/version.h @@ -1,8 +1,8 @@ /* */ #define VERSION "1.24" #define VSTRING "1" -#define DATE "01 August 2002" -#define LSMDATE "01Aug02" +#define DATE "04 August 2002" +#define LSMDATE "04Aug02" /* Debug flags */ #define DEBUG 1 -- 2.39.5