-2003-12-xxx Version 1.33 xxNov03
+
+
+14Jan04
+- Update kernstodo
+- Add quit command to restore tree handler.
+- Make restore tree handler remember if a hard link is present, and
+ in doing a mark, only get database entry if there is a hard link.
+ mark commands thus run at least 2 orders of magnitude faster.
+- Add files=xxx field to run command submitted for restore.
+- Add yes to restore run command if either yes or run is command line
+ argument.
+- Make "yes" on command line argument skip prompt for modification of
+ run job.
+- Add markdir and unmarkdir -- both affect only the directory in
+ question and do not do a recursive descent.
+- Make tree command automatically mark all higher level directories to
+ be restored when a directory or a file is selected. Such directories
+ are indicated by preceding the name with a + to indicate that only
+ the directory entry is selected and not the whole directory tree.
+- Modify a few tree commands to walk through all arguments rather than
+ just taking the first one.
+13Jan04
+- Update copyright date on changed files.
+- Make mark command in restore tree run *much* faster by accessing database only
+ if the file actually is hard linked -- determined at time tree it built by
+ keeping a has_link bit in the tree entry.
+12Jan04
+- Modify findlib/makepath.c to create all parent directories with full permissions.
+ This should solve the access problems on restoring files on Win32 systems.
+- Modify restore to report **** Restore Error **** if any error are found.
+ I.e. a file could not be created.
+- Change a few errors into warnings -- e.g. if permissions could not be set, but
+ the file is actually restored.
+11Jan04
+- Replace bsd_queue with dlist in lib/watchdog.c
+- Add Date: header to bsmtp. Thanks to Meno Abels (abels) on SourceForge
+ for reporting this.
+- Remove ap==NULL check in src/lib/var.c as suggested by Christoph Barbian
+ because ap (va_list) is a struct on the Alpha, so the code does not
+ compile.
+10Jan04
+- Implement first cut of polling for a Volume. New Device directive
+ "Volume Poll Interval = xxx" where xxx is a time specification
+ (e.g. 5min). Default is off.
+- Call pthread_cancel on SD msgchan only if the threadid is non-zero.
+07Jan04
+- Change RH autostart scripts to start in Dan's order.
+06Jan04
+- Correct calculation of week of month in scheduler and in scheduled
+ listing.
+04Jan04
+- Fix cancel of job waiting on mount to be a bit cleaner.
+03Jan04
+- Fix seg fault -- don't close db in db_open_database(). It must explicitly
+ be called.
+- Doc updates
+- Allow purge of a single JobId from a Volume (code untested)
+- Implement fix to keep SD from looping complaining about the wrong volume
+ at the end of a tape -- reported by Phil, and fix confirmed by him. I cannot
+ seem to reproduce the bug here.
+- Clean up a few more strcpy()s.
+01Jan04
+- Fix configure.in FD User print per Lars.
+- Doc update.
+- New bacula.spec.in from Scott. Nice!
+- Make RunAfterJob non-fatal if it errs since the Job has really
+ already completed the save.
+- Replace sprintf with bsnprintf in message.c
+31Dec03
+- Note, this change affects only the Win32 FD.
+- Fixed Win32 FD crash due to missing argument in status command. It
+ always crashed if there was a job that had previously run. Thanks to
+ Christopher Hull for finding and diagnosing this problem.
+29Dec03
+- Fix SELECT as indicated by Dan in sql_get.c for returning Volume names.
+- Fix last_jobs list crash for utility programs (btape, ...).
+- Correct editing of FileSet name into SQL command.
+- Allow calling watchdog stop without initialization for utility programs.
+- Enhance btape test by attempting to forward space past the end of the tape.
+- Detect CAP_FASTFSF in btape test as possible reason for failure.
+- Attempt to prevent infinite loops in fsf_dev() if device not properly
+ configured.
+- Attempt to get correct file number on tape after backspacing at EOM. The
+ correction should resolve differences in OS tape driver implementations.
+- Add ctl-c detection to bconsole. When reading from tty, interrupts any
+ output from Bacula, when reading from a file, it terminates the reading
+ and returns possibly exiting if it is a batch job.
+- Create devel_bacula.in script and make it configure itself. Very similar
+ to the new bacula.in.
+- Modify bacula.in to have separate file locations for each daemon -- makes
+ it easier to make a new devel_bacula.in when there are changes.
+- Remove old config files when rebuilding configure to avoid error msgs.
+- Fix btape "test" to respect TWO EOF.
+- Fix fsf_dev() to use system notion of file if it is valid after bsf_dev().
+- Move btraceback.gdb to scripts directory on installl.
+- Add new mandrake bacula.spec.in
28Dec03
- Find commonset of c_iflags that work with FreeBSD tcsetattr on terminals.
- Remove comments from string concatenation -- doesn't work on FreeBSD
Bacula code: Total files = 281 Total lines = 83,815 (*.h *.c *.in)
Most Significant Changes since 1.32d
+- Dan Langille has written a PostgreSQL driver for Bacula.
- Implement "update slots scan" that reads the volume label(s).
- The full form of the scan is "scan=1,2,4-5,7". With no specification,
all occupied slots are scanned.
tape block, so the tape is not recognized.
Other Changes since 1.32d
+- Restore directory tree automatically selects all higher level
+ directories to be restored.
- Implement conio.c to use in console program -- mini-readline.
- Enhance "fill" command of btape -- simpler output. Use -v to
cause last block to be dumped after write and after re-read.
Kern's ToDo List
- 09 January 2004
+ 13 January 2004
Documentation to do: (any release a little bit at a time)
- Document running a test version.
- Add db check test to regression. Test each function like delete,
purge, ...
- Add subsections to the Disaster Recovery index section.
+- Document Pool keyword for restore.
For 1.33
+- Finish code passing files=nnn to restore start.
+- Add Console usr permissions -- do by adding regex filters for
+ jobs, clients, storage, ...
+- Put max network buffer size on a directive.
- Why does "mark cygwin" take so long!!!!!!!!
- When a file is set for restore, walk back up the chain of
directories, setting them to be restored.
Job report (Volker Sauer).
- Client does not show busy during Estimate command.
- Implement Console mtx commands.
-- Look at 2Gb limit for SQLite.
- Implement 3 Pools for a Job:
Job {
Name = ...
GRANT all privileges ON bacula.* TO bacula@localhost IDENTIFIED BY
'bacula_password';
FLUSH PRIVILEGES;
-- Define week of year for scheduler. W01, W02, ...
- Week 01 of a year is per definition the first week that has the
- Thursday in this year, which is equivalent to the week that contains the
- fourth day of January. In other words, the first week of a new year is
- the week that has the majority of its days in the new year. Week 01
- might also contain days from the previous year and the week before week
- 01 of a year is the last week (52 or 53) of the previous year even if it
- contains days from the new year. A week starts with Monday (day 1) and
- ends with Sunday (day 7). For example, the first week of the year 1997
- lasts from 1996-12-30 to 1997-01-05 and can be written in standard
- notation as
-
- 1997-W01 or 1997W01
-
- The week notation can also be extended by a number indicating the day
- of the week. For example, the day 1996-12-31, which is the Tuesday (day
- 2) of the first week of 1997, can also be written as
-
- 1997-W01-2 or 1997W012
-
- Implement a Mount Command and an Unmount Command where
the users could specify a system command to be performed
to do the mount, after which Bacula could attempt to
- Take a careful look at Level for the estimate command, maybe make
it a command line option.
- Add Volume name to "I cannot write on this volume because"
-- Make restore job check if all the files are actually restored.
- Make tree walk routines like cd, ls, ... more user friendly
by handling spaces better.
- Write your PID file and chown root:wheel before drop.
- Set IO_NOWAIT on Bacula TCP/IP packets.
- Try doing a raw partition backup and restore by mounting a
Windows partition.
-- Report CVS problems to SourceForge.
-- Implement .consolerc for Console
-- From Lars Köllers:
+- From Lars Kellers:
Yes, it would allow to highly automatic the request for new tapes. If a
tape is empty, bacula reads the barcodes (native or simulated), and if
an unused tape is found, it runs the label command with all the
By the way can bacula automatically "move" an empty/purged volume say
in the "short" pool to the "long" pool if this pool runs out of volume
space?
-- Either restrict the characters in a name, or fix the problem
- emailing with names containing / (smtp command line breaks).
- Eliminate orphaned jobs: dbcheck, normal pruning, delete job command.
Hm. Well, there are the remaining orphaned job records:
- Walk through the Pool records rather than the Job records
in dird.c to create/update pools.
- What to do about "list files job=xxx".
-- Implement scan: for every slot it finds, zero the slot of
- Volume other volume having that slot.
- When job rescheduled, status gives is waiting for Client Rufus
to connect to Storage File. Dir needs to inform SD that job
is rescheduled.
-- Fix get_storage_from_media_type (ua_restore) to use command line
- storage=
-- Don't print "Warning: Wrong Volume mounted ..." if mounting second volume.
- Make Dmsg look at global before calling subroutine.
- Enable trace output at runtime for Win32
- Available volumes for autochangers (see patrick@baanboard.com 3 Sep 03
- Get and test MySQL 4.0
- Do a complete audit of all pthreads_mutex, cond, ... to ensure that
any that are dynamically initialized are destroyed when no longer used.
-- Write a mini-readline with history and editing.
- Look at how fuser works and /proc/PID/fd that is how Nic found the
file descriptor leak in Bacula.
- Implement WrapCounters in Counters.
- Can we dynamically change FileSets?
- If pool specified to label command and Label Format is specified,
automatically generate the Volume name.
-- Take a careful look a the Basic recycling algorithm. When Bacula
- chooses, the order should be:
- - Look for Append
- - Look for Recycle or Purged
- - Prune volumes
- - Look for purged
- Instead of using lowest media Id, find the least recently used
- volume.
-
- When the tape is mounted and Bacula requests the status
- - Do everything possible to use it.
-
- Define a "available" status, which is the currently mounted
- Volume and all volumes that are currently in the autochanger.
-
- Why can't SQL do the filename sort for restore?
-- Is a pool specification really needed for a restore? Yes, and
- you may want to exclude archive Pools.
- Look at libkse (man kse) for FreeBSD threading.
- Look into Microsoft Volume Shadowcopy Service VSS for backing
up system state components (Active Directory, System Volume, ...)
time as the user walks through the tree).
- Possibly use the hash code if the user selects all for a restore command.
- Orphaned Dir buffer at parse_conf.c:373 => store_dir
-- Add Console usr permissions.
- Fix "restore all" to bypass building the tree.
-- Fix restore to list errors if Invalid block found, and if # files
- restored does not match # expected.
- Prohibit backing up archive device (findlib/find_one.c:128)
- Implement Release Device in the Job resource to unmount a drive.
- Implement Acquire Device in the Job resource to mount a drive,
- Make things like list where a file is saved case independent for
Windows.
- Implement migrate
-- Implement a PostgreSQL driver.
- Bacula needs to propagate SD errors.
> > cluster-dir: Start Backup JobId 252, Job=REUTERS.2003-08-11_15.04.12
> > prod4-sd: REUTERS.2003-08-11_15.04.12 Error: Write error on device
We must cd into the directory then create the file without the
full path name.
- lstat() is not going to work on Win32 for testing date.
-- Something is not right in last block of fill command.
- Implement a Recycle command
-- Add FileSet to command line arguments for restore.
- Add client name to cram-md5 challenge so Director can immediately
verify if it is the correct client.
- Add JobLevel in FD status (but make sure it is defined).
- Implement restore "current system", but take all files without
doing selection tree -- so that jobs without File records can
be restored.
-- Implement a relocatable bacula.spec
- Add prefixlinks to where or not where absolute links to FD.
- Issue message to mount a new tape before the rewind.
- Simplified client job initiation for portables.
- Add UA rc and history files.
- put termcap (used by console) in ./configure and
allow -with-termcap-dir.
-- Enhance time and size scanning routines.
- Fix Autoprune for Volumes to respect need for full save.
- Fix Win32 config file definition name on /install
- Compare tape to Client files (attributes, or attributes and data)
=============================================================
- Request For Comments For File Backup Options
+ Request For Comments For File Backup Options
10 November 2002
Subject: File Backup Options
- Implement ClientRunBeforeJob and ClientRunAfterJob.
- Implement forward spacing block/file: position_device(bsr) --
just before read_block_from_device();
+
=== for 1.33
- Change console to bconsole.
- Change smtp to bsmtp.
Could I get you to double check the switch () statements in the
job_check_maxwaittime and job_check_maxruntime functions in
src/dird/job.c?
+- Define week of year for scheduler. W01, W02, ...
+ Week 01 of a year is per definition the first week that has the
+ Thursday in this year, which is equivalent to the week that contains the
+ fourth day of January. In other words, the first week of a new year is
+ the week that has the majority of its days in the new year. Week 01
+ might also contain days from the previous year and the week before week
+ 01 of a year is the last week (52 or 53) of the previous year even if it
+ contains days from the new year. A week starts with Monday (day 1) and
+ ends with Sunday (day 7). For example, the first week of the year 1997
+ lasts from 1996-12-30 to 1997-01-05 and can be written in standard
+ notation as
+ 1997-W01 or 1997W01
+ The week notation can also be extended by a number indicating the day
+ of the week. For example, the day 1996-12-31, which is the Tuesday (day
+ 2) of the first week of 1997, can also be written as
+ 1997-W01-2 or 1997W012
+- Either restrict the characters in a name, or fix the problem
+ emailing with names containing / (smtp command line breaks).
+- Implement .consolerc for Console
+- Implement scan: for every slot it finds, zero the slot of
+ Volume other volume having that slot.
+- Make restore job check if all the files are actually restored.
+- Look at 2Gb limit for SQLite.
+- Fix get_storage_from_media_type (ua_restore) to use command line
+ storage=
+- Don't print "Warning: Wrong Volume mounted ..." if mounting second volume.
+- Write a mini-readline with history and editing.
+- Take a careful look a the Basic recycling algorithm. When Bacula
+ chooses, the order should be:
+ - Look for Append
+ - Look for Recycle or Purged
+ - Prune volumes
+ - Look for purged
+ Instead of using lowest media Id, find the least recently used
+ volume.
+
+ When the tape is mounted and Bacula requests the status
+ - Do everything possible to use it.
+
+ Define a "available" status, which is the currently mounted
+ Volume and all volumes that are currently in the autochanger.
+- Is a pool specification really needed for a restore? Yes, and
+ you may want to exclude archive Pools.
+- Implement a PostgreSQL driver.
+- Fix restore to list errors if Invalid block found, and if # files
+ restored does not match # expected.
+- Something is not right in last block of fill command.
+- Add FileSet to command line arguments for restore.
+- Enhance time and size scanning routines.
/* fd_cmds.c */
extern int connect_to_file_daemon(JCR *jcr, int retry_interval,
- int max_retry_time, int verbose);
+ int max_retry_time, int verbose);
extern int send_include_list(JCR *jcr);
extern int send_exclude_list(JCR *jcr);
extern int send_bootstrap_file(JCR *jcr);
extern int get_attributes_and_put_in_catalog(JCR *jcr);
extern int get_attributes_and_compare_to_catalog(JCR *jcr, JobId_t JobId);
extern int put_file_into_catalog(JCR *jcr, long file_index, char *fname,
- char *link, char *attr, int stream);
+ char *link, char *attr, int stream);
extern void get_level_since_time(JCR *jcr, char *since, int since_len);
extern int send_run_before_and_after_commands(JCR *jcr);
/* msgchan.c */
extern int connect_to_storage_daemon(JCR *jcr, int retry_interval,
- int max_retry_time, int verbose);
+ int max_retry_time, int verbose);
extern int start_storage_daemon_job(JCR *jcr);
extern int start_storage_daemon_message_thread(JCR *jcr);
extern int bget_dirmsg(BSOCK *bs);
void free_ua_context(UAContext *ua);
/* ua_select.c */
-STORE *select_storage_resource(UAContext *ua);
-JOB *select_job_resource(UAContext *ua);
-JOB *select_restore_job_resource(UAContext *ua);
-CLIENT *select_client_resource(UAContext *ua);
+STORE *select_storage_resource(UAContext *ua);
+JOB *select_job_resource(UAContext *ua);
+JOB *select_restore_job_resource(UAContext *ua);
+CLIENT *select_client_resource(UAContext *ua);
FILESET *select_fileset_resource(UAContext *ua);
-int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
-int select_media_dbr(UAContext *ua, MEDIA_DBR *mr);
-int select_pool_dbr(UAContext *ua, POOL_DBR *pr);
-int select_client_dbr(UAContext *ua, CLIENT_DBR *cr);
-
-void start_prompt(UAContext *ua, char *msg);
-void add_prompt(UAContext *ua, char *prompt);
-int do_prompt(UAContext *ua, char *automsg, char *msg, char *prompt, int max_prompt);
-CAT *get_catalog_resource(UAContext *ua);
+int select_pool_and_media_dbr(UAContext *ua, POOL_DBR *pr, MEDIA_DBR *mr);
+int select_media_dbr(UAContext *ua, MEDIA_DBR *mr);
+int select_pool_dbr(UAContext *ua, POOL_DBR *pr);
+int select_client_dbr(UAContext *ua, CLIENT_DBR *cr);
+
+void start_prompt(UAContext *ua, char *msg);
+void add_prompt(UAContext *ua, char *prompt);
+int do_prompt(UAContext *ua, char *automsg, char *msg, char *prompt, int max_prompt);
+CAT *get_catalog_resource(UAContext *ua);
STORE *get_storage_resource(UAContext *ua, int use_default);
-int get_media_type(UAContext *ua, char *MediaType, int max_media);
-int get_pool_dbr(UAContext *ua, POOL_DBR *pr);
-int get_client_dbr(UAContext *ua, CLIENT_DBR *cr);
+int get_media_type(UAContext *ua, char *MediaType, int max_media);
+int get_pool_dbr(UAContext *ua, POOL_DBR *pr);
+int get_client_dbr(UAContext *ua, CLIENT_DBR *cr);
POOL *get_pool_resource(UAContext *ua);
POOL *select_pool_resource(UAContext *ua);
CLIENT *get_client_resource(UAContext *ua);
-int get_job_dbr(UAContext *ua, JOB_DBR *jr);
+int get_job_dbr(UAContext *ua, JOB_DBR *jr);
int find_arg_keyword(UAContext *ua, char **list);
int find_arg(UAContext *ua, char *keyword);
int confirm_retention(UAContext *ua, utime_t *ret, char *msg);
/* ua_tree.c */
-void user_select_files_from_tree(TREE_CTX *tree);
+bool user_select_files_from_tree(TREE_CTX *tree);
int insert_tree_handler(void *ctx, int num_fields, char **row);
/* ua_prune.c */
JCR *jcr;
B_DB *db;
CAT *catalog;
- POOLMEM *cmd; /* return command/name buffer */
- POOLMEM *args; /* command line arguments */
- char *argk[MAX_CMD_ARGS]; /* argument keywords */
- char *argv[MAX_CMD_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 quit; /* if set, quit */
- int verbose; /* set for normal UA verbosity */
- uint32_t pint32_val; /* positive integer */
- int32_t int32_val; /* positive/negative */
-};
+ POOLMEM *cmd; /* return command/name buffer */
+ POOLMEM *args; /* command line arguments */
+ char *argk[MAX_CMD_ARGS]; /* argument keywords */
+ char *argv[MAX_CMD_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 */
+ bool auto_display_messages; /* if set, display messages */
+ bool user_notified_msg_pending; /* set when user notified */
+ bool automount; /* if set, mount after label */
+ bool quit; /* if set, quit */
+ bool verbose; /* set for normal UA verbosity */
+ uint32_t pint32_val; /* positive integer */
+ int32_t int32_val; /* positive/negative */
+};
/* Context for insert_tree_handler() */
struct TREE_CTX {
- TREE_ROOT *root; /* root */
- TREE_NODE *node; /* current node */
- TREE_NODE *avail_node; /* unused node last insert */
- int cnt; /* count for user feedback */
+ TREE_ROOT *root; /* root */
+ TREE_NODE *node; /* current node */
+ TREE_NODE *avail_node; /* unused node last insert */
+ int cnt; /* count for user feedback */
UAContext *ua;
};
static void free_name_list(NAME_LIST *name_list);
static void get_storage_from_mediatype(UAContext *ua, NAME_LIST *name_list, RESTORE_CTX *rx);
static int select_backups_before_date(UAContext *ua, RESTORE_CTX *rx, char *date);
-static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
+static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx);
static void free_rx(RESTORE_CTX *rx);
static void split_path_and_filename(RESTORE_CTX *rx, char *fname);
static int jobid_fileindex_handler(void *ctx, int num_fields, char **row);
case 0:
goto bail_out;
case 1: /* select by jobid */
- build_directory_tree(ua, &rx);
+ if (!build_directory_tree(ua, &rx)) {
+ bsendmsg(ua, _("Restore not done.\n"));
+ goto bail_out;
+ }
break;
case 2: /* select by filename, no tree needed */
break;
if (rx.where) {
Mmsg(&ua->cmd,
"run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\""
- " where=\"%s\"",
+ " where=\"%s\" files=%d",
job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
- working_directory, rx.where);
+ working_directory, rx.where, rx.selected_files);
} else {
Mmsg(&ua->cmd,
"run job=\"%s\" client=\"%s\" storage=\"%s\" bootstrap=\"%s/restore.bsr\"",
job->hdr.name, rx.ClientName, rx.store?rx.store->hdr.name:"",
working_directory);
}
- if (find_arg(ua, _("run")) >= 0) {
- pm_strcat(&ua->cmd, " run"); /* pass it on to the run command */
+ if (find_arg(ua, _("run")) >= 0 || find_arg(ua, _("yes"))) {
+ pm_strcat(&ua->cmd, " yes"); /* pass it on to the run command */
}
Dmsg1(400, "Submitting: %s\n", ua->cmd);
parse_ua_args(ua);
Dmsg2(100, "sllit path=%s file=%s\n", rx->path, rx->fname);
}
-static void build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
+static bool build_directory_tree(UAContext *ua, RESTORE_CTX *rx)
{
TREE_CTX tree;
JobId_t JobId, last_JobId;
char *p;
char *nofname = "";
+ bool OK = true;
memset(&tree, 0, sizeof(TREE_CTX));
/*
get_storage_from_mediatype(ua, &rx->name_list, rx);
if (find_arg(ua, _("all")) < 0) {
- /* Let the user select which files to restore */
- user_select_files_from_tree(&tree);
+ /* Let the user interact in selecting which files to restore */
+ OK = user_select_files_from_tree(&tree);
}
/*
* Walk down through the tree finding all files marked to be
* extracted making a bootstrap file.
*/
- 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(rx->bsr, node->JobId, node->FileIndex);
- rx->selected_files++;
+ if (OK) {
+ 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 || node->extract_dir) {
+ Dmsg2(400, "type=%d FI=%d\n", node->type, node->FileIndex);
+ add_findex(rx->bsr, node->JobId, node->FileIndex);
+ rx->selected_files++;
+ }
}
}
free_tree(tree.root); /* free the directory tree */
+ return OK;
}
N_("when"), /* 12 */
N_("priority"), /* 13 */
N_("yes"), /* 14 -- if you change this change YES_POS too */
- N_("run"), /* 15 -- if you change this change RUN_POS too */
N_("verifyjob"), /* 16 */
NULL};
#define YES_POS 14
-#define RUN_POS 15
if (!open_db(ua)) {
return 1;
for (j=0; !found && kw[j]; j++) {
if (strcasecmp(ua->argk[i], _(kw[j])) == 0) {
/* Note, yes and run have no value, so do not err */
- if (!ua->argv[i] && (j != YES_POS /*yes*/ && j != RUN_POS)) {
+ if (!ua->argv[i] && j != YES_POS /*yes*/) {
bsendmsg(ua, _("Value missing for keyword %s\n"), ua->argk[i]);
return 1;
}
}
break;
case 14: /* yes */
- case 15: /* run */
found = true;
break;
- case 16: /* Verify Job */
+ case 15: /* Verify Job */
if (verify_job_name) {
bsendmsg(ua, _("Verify Job specified twice.\n"));
return 1;
replace = ReplaceOptions[i].name;
}
}
+ if (level_name) {
+ /* Look up level name and pull code */
+ found = 0;
+ for (i=0; joblevels[i].level_name; i++) {
+ if (strcasecmp(level_name, _(joblevels[i].level_name)) == 0) {
+ jcr->JobLevel = joblevels[i].level;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ bsendmsg(ua, _("Level %s not valid.\n"), level_name);
+ goto bail_out;
+ }
+ }
+ level_name = NULL;
+ if (jid) {
+ jcr->RestoreJobId = atoi(jid);
+ }
+
+ /* Run without prompting? */
+ if (find_arg(ua, _("yes")) > 0) {
+ Dmsg1(200, "Calling run_job job=%x\n", jcr->job);
+ run_job(jcr);
+ free_jcr(jcr); /* release jcr */
+ bsendmsg(ua, _("Run command submitted.\n"));
+ return 1;
+ }
+
+ /*
+ * Prompt User to see if all run job parameters are correct, and
+ * allow him to modify them.
+ */
Dmsg1(20, "JobType=%c\n", jcr->JobType);
switch (jcr->JobType) {
char ec1[30];
break;
case JT_BACKUP:
case JT_VERIFY:
- if (level_name) {
- /* Look up level name and pull code */
- found = 0;
- for (i=0; joblevels[i].level_name; i++) {
- if (strcasecmp(level_name, _(joblevels[i].level_name)) == 0) {
- jcr->JobLevel = joblevels[i].level;
- found = 1;
- break;
- }
- }
- if (!found) {
- bsendmsg(ua, _("Level %s not valid.\n"), level_name);
- goto bail_out;
- }
- }
- level_name = NULL;
if (jcr->JobType == JT_BACKUP) {
bsendmsg(ua, _("Run %s job\n\
JobName: %s\n\
goto bail_out;
}
- /* Run without prompting? */
- if (find_arg(ua, _("yes")) > 0) {
- Dmsg1(200, "Calling run_job job=%x\n", jcr->job);
- run_job(jcr);
- free_jcr(jcr); /* release jcr */
- bsendmsg(ua, _("Run command submitted.\n"));
- return 1;
- }
if (!get_cmd(ua, _("OK to run? (yes/mod/no): "))) {
goto bail_out;
/* Forward referenced commands */
static int markcmd(UAContext *ua, TREE_CTX *tree);
+static int markdircmd(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 lsmark(UAContext *ua, TREE_CTX *tree);
+static int lsmarkcmd(UAContext *ua, TREE_CTX *tree);
static int dircmd(UAContext *ua, TREE_CTX *tree);
static int estimatecmd(UAContext *ua, TREE_CTX *tree);
static int helpcmd(UAContext *ua, TREE_CTX *tree);
static int cdcmd(UAContext *ua, TREE_CTX *tree);
static int pwdcmd(UAContext *ua, TREE_CTX *tree);
static int unmarkcmd(UAContext *ua, TREE_CTX *tree);
+static int unmarkdircmd(UAContext *ua, TREE_CTX *tree);
static int quitcmd(UAContext *ua, TREE_CTX *tree);
+static int donecmd(UAContext *ua, TREE_CTX *tree);
struct cmdstruct { char *key; int (*func)(UAContext *ua, TREE_CTX *tree); char *help; };
static struct cmdstruct commands[] = {
{ N_("cd"), cdcmd, _("change current directory")},
- { N_("count"), countcmd, _("count marked files")},
+ { N_("count"), countcmd, _("count marked files in and below the cd")},
{ N_("dir"), dircmd, _("list current directory")},
- { N_("done"), quitcmd, _("leave file selection mode")},
+ { N_("done"), donecmd, _("leave file selection mode")},
{ N_("estimate"), estimatecmd, _("estimate restore size")},
- { N_("exit"), quitcmd, _("exit = done")},
- { N_("find"), findcmd, _("find files")},
+ { N_("exit"), donecmd, _("exit = done")},
+ { N_("find"), findcmd, _("find files -- wildcards allowed")},
{ N_("help"), helpcmd, _("print help")},
- { N_("lsmark"), lsmark, _("list the marked files")},
- { N_("ls"), lscmd, _("list current directory")},
+ { N_("lsmark"), lsmarkcmd, _("list the marked files in and below the cd")},
+ { N_("ls"), lscmd, _("list current directory -- wildcards allowed")},
{ N_("mark"), markcmd, _("mark file to be restored")},
+ { N_("markdir"), markdircmd, _("mark directory entry to be restored -- nonrecursive")},
{ N_("pwd"), pwdcmd, _("print current working directory")},
{ N_("unmark"), unmarkcmd, _("unmark file to be restored")},
+ { N_("unmarkdir"), unmarkdircmd, _("unmark directory -- no recursion")},
+ { N_("quit"), quitcmd, _("quit")},
{ N_("?"), helpcmd, _("print help")},
};
#define comsize (sizeof(commands)/sizeof(struct cmdstruct))
* files to be restored. This is sort of like a mini-shell
* that allows "cd", "pwd", "add", "rm", ...
*/
-void user_select_files_from_tree(TREE_CTX *tree)
+bool user_select_files_from_tree(TREE_CTX *tree)
{
char cwd[2000];
+ bool stat;
/* Get a new context so we don't destroy restore command args */
UAContext *ua = new_ua_context(tree->ua->jcr);
ua->UA_sock = tree->ua->UA_sock; /* patch in UA socket */
tree_getpath(tree->node, cwd, sizeof(cwd));
bsendmsg(tree->ua, _("cwd is: %s\n"), cwd);
for ( ;; ) {
- int found, len, stat, i;
+ int found, len, i;
if (!get_cmd(ua, "$ ")) {
break;
}
len = strlen(ua->argk[0]);
found = 0;
- stat = 0;
+ stat = false;
for (i=0; i<(int)comsize; i++) /* search for command */
if (strncasecmp(ua->argk[0], _(commands[i].key), len) == 0) {
stat = (*commands[i].func)(ua, tree); /* go execute command */
}
}
ua->UA_sock = NULL; /* don't release restore socket */
+ stat = !ua->quit;
+ ua->quit = false;
free_ua_context(ua); /* get rid of temp UA context */
+ return stat;
}
new_node->JobId = (JobId_t)str_to_int64(row[3]);
new_node->type = type;
new_node->extract = true; /* extract all by default */
- new_node->extract_dir = true; /* if dir, extract it */
+ if (type == TN_DIR || type == TN_DIR_NLS) {
+ new_node->extract_dir = true; /* if dir, extract it */
+ }
new_node->have_link = (decode_LinkFI(row[4]) != 0);
tree->cnt++;
return 0;
int count = 0;
node->extract = extract;
+ if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
+ node->extract_dir = extract; /* set/clear dir too */
+ }
if (node->type != TN_NEWDIR) {
count++;
}
/* For a non-file (i.e. directory), we see all the children */
if (node->type != TN_FILE) {
+ /* Recursive set children within directory */
for (n=node->child; n; n=n->sibling) {
count += set_extract(ua, n, tree, extract);
}
+ /*
+ * Walk up tree marking any unextracted parent to be
+ * extracted.
+ */
+ if (extract) {
+ while (node->parent && !node->parent->extract_dir) {
+ node = node->parent;
+ node->extract_dir = true;
+ }
+ }
} else if (extract) {
char cwd[2000];
/*
for (n=first_tree_node(tree->root); n; n=next_tree_node(n)) {
if (n->FileIndex == LinkFI && n->JobId == node->JobId) {
n->extract = true;
+ if (n->type == TN_DIR || n->type == TN_DIR_NLS) {
+ n->extract_dir = true;
+ }
break;
}
}
return count;
}
+/*
+ * Recursively mark the current directory to be restored as
+ * well as all directories and files below it.
+ */
static int markcmd(UAContext *ua, TREE_CTX *tree)
{
TREE_NODE *node;
return 1;
}
+static int markdircmd(UAContext *ua, TREE_CTX *tree)
+{
+ TREE_NODE *node;
+ int count = 0;
+
+ if (ua->argc < 2 || !tree->node->child) {
+ bsendmsg(ua, _("No files marked.\n"));
+ return 1;
+ }
+ for (int i=1; i < ua->argc; i++) {
+ for (node = tree->node->child; node; node=node->sibling) {
+ if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
+ if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
+ node->extract_dir = true;
+ count++;
+ }
+ }
+ }
+ }
+ if (count == 0) {
+ bsendmsg(ua, _("No directories marked.\n"));
+ } else {
+ bsendmsg(ua, _("%d director%s marked.\n"), count, count==1?"y":"ies");
+ }
+ return 1;
+}
+
+
static int countcmd(UAContext *ua, TREE_CTX *tree)
{
int total, num_extract;
for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
if (node->type != TN_NEWDIR) {
total++;
- if (node->extract) {
+ if (node->extract || node->extract_dir) {
num_extract++;
}
}
}
- bsendmsg(ua, "%d total files. %d marked to be restored.\n", total, num_extract);
+ bsendmsg(ua, "%d total files/dirs. %d marked to be restored.\n", total, num_extract);
return 1;
}
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) {
+ char *tag;
tree_getpath(node, cwd, sizeof(cwd));
- bsendmsg(ua, "%s%s\n", node->extract?"*":"", cwd);
+ if (node->extract) {
+ tag = "*";
+ } else if (node->extract_dir) {
+ tag = "+";
+ } else {
+ tag = "";
+ }
+ bsendmsg(ua, "%s%s\n", tag, cwd);
}
}
}
}
for (node = tree->node->child; node; node=node->sibling) {
if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
- bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
+ char *tag;
+ if (node->extract) {
+ tag = "*";
+ } else if (node->extract_dir) {
+ tag = "+";
+ } else {
+ tag = "";
+ }
+ bsendmsg(ua, "%s%s%s\n", tag, node->fname,
(node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
}
}
/*
* Ls command that lists only the marked files
*/
-static int lsmark(UAContext *ua, TREE_CTX *tree)
+static int lsmarkcmd(UAContext *ua, TREE_CTX *tree)
{
TREE_NODE *node;
return 1;
}
for (node = tree->node->child; node; node=node->sibling) {
- if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0 &&
- node->extract) {
- bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
+ if ((ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) &&
+ (node->extract || node->extract_dir)) {
+ char *tag;
+ if (node->extract) {
+ tag = "*";
+ } else if (node->extract_dir) {
+ tag = "+";
+ } else {
+ tag = "";
+ }
+ bsendmsg(ua, "%s%s%s\n", tag, node->fname,
(node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
}
}
/*
* This is actually the long form used for "dir"
*/
-static void ls_output(char *buf, char *fname, bool extract, struct stat *statp)
+static void ls_output(char *buf, char *fname, char *tag, struct stat *statp)
{
char *p, *f;
char ec1[30];
p += n;
p = encode_time(statp->st_ctime, p);
*p++ = ' ';
- if (extract) {
- *p++ = '*';
- } else {
- *p++ = ' ';
- }
- for (f=fname; *f; )
+ *p++ = *tag;
+ for (f=fname; *f; ) {
*p++ = *f++;
+ }
*p = 0;
}
return 1;
}
for (node = tree->node->child; node; node=node->sibling) {
+ char *tag;
if (ua->argc == 1 || fnmatch(ua->argk[1], node->fname, 0) == 0) {
+ if (node->extract) {
+ tag = "*";
+ } else if (node->extract_dir) {
+ tag = "+";
+ } else {
+ tag = " ";
+ }
tree_getpath(node, cwd, sizeof(cwd));
fdbr.FileId = 0;
fdbr.JobId = node->JobId;
if (db_get_file_attributes_record(ua->jcr, ua->db, cwd, NULL, &fdbr)) {
int32_t LinkFI;
decode_stat(fdbr.LStat, &statp, &LinkFI); /* decode stat pkt */
- ls_output(buf, cwd, node->extract, &statp);
+ ls_output(buf, cwd, tag, &statp);
bsendmsg(ua, "%s\n", buf);
} else {
/* Something went wrong getting attributes -- print name */
- bsendmsg(ua, "%s%s%s\n", node->extract?"*":"", node->fname,
+ if (*tag == ' ') {
+ tag = "";
+ }
+ bsendmsg(ua, "%s%s%s\n", tag, node->fname,
(node->type==TN_DIR||node->type==TN_NEWDIR)?"/":"");
}
}
}
}
/* Directory, count only */
- } else if (node->extract) {
+ } else if (node->extract || node->extract_dir) {
num_extract++;
}
}
return 1;
}
+static int unmarkdircmd(UAContext *ua, TREE_CTX *tree)
+{
+ TREE_NODE *node;
+ int count = 0;
+
+ if (ua->argc < 2 || !tree->node->child) {
+ bsendmsg(ua, _("No directories unmarked.\n"));
+ return 1;
+ }
+
+ for (int i=1; i < ua->argc; i++) {
+ for (node = tree->node->child; node; node=node->sibling) {
+ if (fnmatch(ua->argk[i], node->fname, 0) == 0) {
+ if (node->type == TN_DIR || node->type == TN_DIR_NLS) {
+ node->extract_dir = false;
+ count++;
+ }
+ }
+ }
+ }
+
+ if (count == 0) {
+ bsendmsg(ua, _("No directories unmarked.\n"));
+ } else {
+ bsendmsg(ua, _("%d director%s unmarked.\n"), count, count==1?"y":"ies");
+ }
+ return 1;
+}
+
+
+static int donecmd(UAContext *ua, TREE_CTX *tree)
+{
+ return 0;
+}
+
static int quitcmd(UAContext *ua, TREE_CTX *tree)
{
+ ua->quit = true;
return 0;
}
#undef VERSION
#define VERSION "1.33"
#define VSTRING "1"
-#define BDATE "12 Jan 2004"
-#define LSMDATE "12Jan04"
+#define BDATE "14 Jan 2004"
+#define LSMDATE "14Jan04"
/* Debug flags */
#undef DEBUG