rm -f /etc/rc.local.$$today; \
cp -p /etc/rc.local /etc/rc.local.$$today; \
( echo "Start the Bacula Storage daemon. Do not remove the 'TAG_BACULA_SD' text"; \
- echo "if [ -x /etc/rc.bacula-fd ]; then # TAG_BACULA_SD"; \
- echo " /etc/rc.bacula-fd start # TAG_BACULA_SD"; \
+ echo "if [ -x /etc/rc.bacula-sd ]; then # TAG_BACULA_SD"; \
+ echo " /etc/rc.bacula-sd start # TAG_BACULA_SD"; \
echo "fi # TAG_BACULA_SD"; \
) >> /etc/rc.local; \
echo ""; \
devclean: clean
@rm -f Makefile bacula-*.spec
-
m_msg(file, line, &mdb->errmsg, _("Update problem: affected_rows=%s\n"),
edit_uint64(mdb->num_rows, ed1));
if (verbose) {
- j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
+// j_msg(file, line, jcr, M_INFO, 0, "%s\n", cmd);
}
return 0;
}
/* ======= ua_restore.c */
+const char *uar_count_files =
+ "SELECT JobFiles FROM Job WHERE JobId=%u";
/* List last 20 Jobs */
const char *uar_list_jobs =
JCR *jcr;
B_DB *db;
CAT *catalog;
- CONRES *cons; /* console resource */
- 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 */
+ CONRES *cons; /* console resource */
+ 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 */
-};
+ 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 */
- bool all; /* if set mark all as default */
+ TREE_ROOT *root; /* root */
+ TREE_NODE *node; /* current node */
+ TREE_NODE *avail_node; /* unused node last insert */
+ int cnt; /* count for user feedback */
+ bool all; /* if set mark all as default */
UAContext *ua;
+ uint32_t FileEstimate; /* estimate of number of files */
+ uint32_t FileCount; /* current count of files */
+ uint32_t LastCount; /* last count of files */
+ uint32_t DeltaCount; /* trigger for printing */
};
#endif
extern char *uar_inc, *uar_list_temp, *uar_sel_jobid_temp;
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;
struct NAME_LIST {
static void insert_one_file(UAContext *ua, RESTORE_CTX *rx, char *date);
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);
/*
* Restore files
* appear more than once, however, we only insert it once.
*/
int items = 0;
+ p = rx->JobIds;
+ tree.FileEstimate = 0;
+ if (get_next_jobid_from_list(&p, &JobId) > 0) {
+ /* Use first JobId as estimate of the number of files to restore */
+ Mmsg(&rx->query, uar_count_files, JobId);
+ if (!db_sql_query(ua->db, rx->query, count_handler, (void *)rx)) {
+ bsendmsg(ua, "%s\n", db_strerror(ua->db));
+ }
+ if (rx->found) {
+ /* Add about 25% more than this job for over estimate */
+ tree.FileEstimate = rx->JobId + (rx->JobId >> 2);
+ tree.DeltaCount = rx->JobId/50; /* print 50 ticks */
+ }
+ }
for (p=rx->JobIds; get_next_jobid_from_list(&p, &JobId) > 0; ) {
if (JobId == last_JobId) {
bsendmsg(ua, "%s", db_strerror(ua->db));
}
}
- bsendmsg(ua, "%d Job%s inserted into the tree%s.\n",
- items, items==1?"":"s", tree.all?" and marked for extraction":"");
+ 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":"");
/* Check MediaType and select storage that corresponds */
get_storage_from_mediatype(ua, &rx->name_list, rx);
return 1;
}
+static int count_handler(void *ctx, int num_fields, char **row)
+{
+ RESTORE_CTX *rx = (RESTORE_CTX *)ctx;
+ rx->JobId = atoi(row[0]);
+ rx->found = true;
+ return 0;
+}
+
/*
* Callback handler to get JobId and FileIndex for files
*/
{ N_("help"), helpcmd, _("print help")},
{ N_("ls"), lscmd, _("list current directory -- wildcards allowed")},
{ N_("lsmark"), lsmarkcmd, _("list the marked files in and below the cd")},
- { N_("mark"), markcmd, _("mark file to be restored")},
- { N_("markdir"), markdircmd, _("mark directory entry to be restored -- nonrecursive")},
+ { N_("mark"), markcmd, _("mark dir/file to be restored -- recursively in dirs")},
+ { N_("markdir"), markdircmd, _("mark directory name to be restored (no files)")},
{ N_("pwd"), pwdcmd, _("print current working directory")},
- { N_("unmark"), unmarkcmd, _("unmark file to be restored")},
- { N_("unmarkdir"), unmarkdircmd, _("unmark directory -- no recursion")},
+ { N_("unmark"), unmarkcmd, _("unmark dir/file to be restored -- recursively in dir")},
+ { N_("unmarkdir"), unmarkdircmd, _("unmark directory name only -- no recursion")},
{ N_("quit"), quitcmd, _("quit")},
{ N_("?"), helpcmd, _("print help")},
};
type = TN_FILE;
}
hard_link = (decode_LinkFI(row[4], &statp) != 0);
- node = insert_tree_node(row[0], row[1], NULL, tree->root, NULL);
- /* Note, if node already exists, save new one for next time */
+ node = insert_tree_node(row[0], row[1], type, tree->root, NULL);
JobId = (JobId_t)str_to_int64(row[3]);
FileIndex = atoi(row[2]);
/*
}
}
}
+ if (node->inserted) {
+ tree->FileCount++;
+ if (tree->DeltaCount > 0 && (tree->FileCount-tree->LastCount) > tree->DeltaCount) {
+ bsendmsg(tree->ua, "+");
+ tree->LastCount = tree->FileCount;
+ }
+ }
tree->cnt++;
return 0;
}
{
TREE_NODE *node;
int count = 0;
+ char ec1[50];
if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
bsendmsg(ua, _("No files marked.\n"));
if (count == 0) {
bsendmsg(ua, _("No files marked.\n"));
} else {
- bsendmsg(ua, _("%d file%s marked.\n"), count, count==0?"":"s");
+ bsendmsg(ua, _("%s file%s marked.\n"),
+ edit_uint64_with_commas(count, ec1), count==0?"":"s");
}
return 1;
}
{
TREE_NODE *node;
int count = 0;
+ char ec1[50];
if (ua->argc < 2 || !tree_node_has_child(tree->node)) {
bsendmsg(ua, _("No files marked.\n"));
if (count == 0) {
bsendmsg(ua, _("No directories marked.\n"));
} else {
- bsendmsg(ua, _("%d director%s marked.\n"), count, count==1?"y":"ies");
+ bsendmsg(ua, _("%s director%s marked.\n"),
+ edit_uint64_with_commas(count, ec1), count==1?"y":"ies");
}
return 1;
}
static int countcmd(UAContext *ua, TREE_CTX *tree)
{
int total, num_extract;
+ char ec1[50];
total = num_extract = 0;
for (TREE_NODE *node=first_tree_node(tree->root); node; node=next_tree_node(node)) {
}
}
}
- bsendmsg(ua, "%d total files/dirs. %d marked to be restored.\n", total, num_extract);
+ bsendmsg(ua, "%s total files/dirs. %d marked to be restored.\n", total,
+ edit_uint64_with_commas(num_extract, ec1));
return 1;
}
GtkWidget *combo;
combo = lookup_widget(dialog, combo_name);
- if (combo) {
+ if (combo && options) {
gtk_combo_set_popdown_strings(GTK_COMBO(combo), options);
}
return;
dir_dialog = create_SelectDirectorDialog();
combo = lookup_widget(dir_dialog, "combo1");
dir_select = lookup_widget(dir_dialog, "dirselect");
- gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);
+ if (dirs) {
+ gtk_combo_set_popdown_strings(GTK_COMBO(combo), dirs);
+ }
gtk_widget_show(dir_dialog);
gtk_main();
/* Forward referenced subroutines */
static TREE_NODE *search_and_insert_tree_node(char *fname, int type,
- TREE_NODE *node, TREE_ROOT *root, TREE_NODE *parent);
+ TREE_ROOT *root, TREE_NODE *parent);
static char *tree_alloc(TREE_ROOT *root, int size);
/*
/*
* Create a new tree node.
*/
-static TREE_NODE *new_tree_node(TREE_ROOT *root, int type)
+static TREE_NODE *new_tree_node(TREE_ROOT *root)
{
TREE_NODE *node;
int size = sizeof(TREE_NODE);
return node;
}
+#ifdef USE_DLIST
+/*
+ * This routine can be called to release the
+ * previously allocated tree node.
+ */
+static void free_tree_node(TREE_ROOT *root)
+{
+ int asize = BALIGN(sizeof(TREE_NODE));
+ root->mem->rem += asize;
+ root->mem->mem -= asize;
+}
+#endif
+
/*
* called when building a tree.
*
*/
-TREE_NODE *insert_tree_node(char *path, char *fname, TREE_NODE *node,
+TREE_NODE *insert_tree_node(char *path, char *fname, int type,
TREE_ROOT *root, TREE_NODE *parent)
{
char *p, *q;
int path_len = strlen(path);
+ TREE_NODE *node;
Dmsg1(100, "insert_tree_node: %s\n", path);
/*
fname = path;
if (!parent) {
parent = (TREE_NODE *)root;
- node->type = TN_DIR_NLS;
+ type = TN_DIR_NLS;
}
Dmsg1(100, "No / found: %s\n", path);
}
- node = search_and_insert_tree_node(fname, 0, node, root, parent);
+ node = search_and_insert_tree_node(fname, 0, root, parent);
if (q) { /* if trailing slash on entry */
*q = '/'; /* restore it */
}
parent = (TREE_NODE *)root;
type = TN_DIR_NLS;
}
- node = search_and_insert_tree_node(fname, type, NULL, root, parent);
+ node = search_and_insert_tree_node(fname, type, root, parent);
return node;
}
+#ifdef USE_DLIST
+static int node_compare(void *item1, void *item2)
+{
+ TREE_NODE *tn1 = (TREE_NODE *)item1;
+ TREE_NODE *tn2 = (TREE_NODE *)item2;
+ if (tn1->fname[0] > tn2->fname[0]) {
+ return 1;
+ } else if (tn1->fname[0] < tn2->fname[0]) {
+ return -1;
+ }
+ return strcmp(tn1->fname, tn2->fname);
+}
+#endif
+
/*
* See if the fname already exists. If not insert a new node for it.
*/
static TREE_NODE *search_and_insert_tree_node(char *fname, int type,
- TREE_NODE *node, TREE_ROOT *root, TREE_NODE *parent)
+ TREE_ROOT *root, TREE_NODE *parent)
{
+#ifdef USE_DLIST
+ TREE_NODE *node, *found_node;
+ node = new_tree_node(root);
+ node->fname = fname;
+ found_node = (TREE_NODE *)parent->child.binary_insert(node, node_compare);
+ if (found_node != node) { /* already in list */
+ free_tree_node(root); /* free node allocated above */
+ found_node->inserted = false;
+ return found_node;
+ }
+ /* It was not found, but is now inserted */
+ node->fname_len = strlen(fname);
+ node->fname = tree_alloc(root, node->fname_len + 1);
+ strcpy(node->fname, fname);
+ node->parent = parent;
+ node->type = type;
+
+ /* Maintain a linear chain of nodes */
+ if (!root->first) {
+ root->first = node;
+ root->last = node;
+ } else {
+ root->last->next = node;
+ root->last = node;
+ }
+ node->inserted = true; /* inserted into tree */
+ return node;
+
+#else
TREE_NODE *sibling, *last_sibling = NULL;
uint16_t fname_len = strlen(fname);
int cmp;
if (cmp < 0) {
/* Insert before current sibling */
if (!node) {
- node = new_tree_node(root, type);
+ node = new_tree_node(root);
}
node->sibling_ = sibling;
if (sibling == first_child(parent)) { /* if sibling was at head of list */
* At this point, the fname is not found. We must add it
*/
if (!node) {
- node = new_tree_node(root, type);
+ node = new_tree_node(root);
}
Dmsg1(000, "append_tree_node: %s\n", fname);
node->fname_len = fname_len;
node->fname = tree_alloc(root, node->fname_len + 1);
strcpy(node->fname, fname);
node->parent = parent;
+ node->type = type;
if (!tree_node_has_child(parent)) {
parent->child_ = node;
} else {
}
node->inserted = true; /* inserted into tree */
return node;
+#endif
}
#ifdef SLOW_WAY
}
Dmsg2(100, "Doing: %d %s\n", type, pathbuf);
- node = new_tree_node(root, type);
+ node = new_tree_node(root);
node->FileIndex = ++FileIndex;
parent = insert_tree_node(pathbuf, node, root, parent);
if (S_ISDIR(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)) {
*/
struct s_mem {
- struct s_mem *next; /* next buffer */
- int rem; /* remaining bytes */
- char *mem; /* memory pointer */
- char first[1]; /* first byte */
+ struct s_mem *next; /* next buffer */
+ int rem; /* remaining bytes */
+ char *mem; /* memory pointer */
+ char first[1]; /* first byte */
};
+#define USE_DLIST
+#ifdef USE_DLIST
+#define foreach_child(var, list) \
+ for((var)=NULL; (*((TREE_NODE **)&(var))=(TREE_NODE*)(list->child.next(var))); )
+
+#define tree_node_has_child(node) \
+ ((node)->child.size() > 0)
+
+#define first_child(node) \
+ ((TREE_NODE *)(node->child.first())
+
+
+/*
+ * Keep this node as small as possible because
+ * there is one for each file.
+ */
+struct s_tree_node {
+ /* KEEP sibling as the first member to avoid having to
+ * do initialization of child */
+ dlink sibling;
+ dlist child;
+ char *fname; /* file name */
+ int32_t FileIndex; /* file index */
+ uint32_t JobId; /* JobId */
+ uint16_t fname_len; /* filename length */
+ int type: 8; /* node type */
+ unsigned int extract: 1; /* extract item */
+ unsigned int extract_dir: 1; /* extract dir entry only */
+ unsigned int hard_link: 1; /* set if have hard link */
+ unsigned int soft_link: 1; /* set if is soft link */
+ unsigned int inserted: 1; /* set when newly inserted */
+ struct s_tree_node *parent;
+ struct s_tree_node *next; /* next hash of FileIndex */
+};
+typedef struct s_tree_node TREE_NODE;
+
+struct s_tree_root {
+ /* KEEP sibling as the first member to avoid having to
+ * do initialization of child */
+ dlink sibling;
+ dlist child;
+ char *fname; /* file name */
+ int32_t FileIndex; /* file index */
+ uint32_t JobId; /* JobId */
+ uint16_t fname_len; /* filename length */
+ unsigned int type: 8; /* node type */
+ unsigned int extract: 1; /* extract item */
+ unsigned int extract_dir: 1; /* extract dir entry only */
+ unsigned int have_link: 1; /* set if have hard link */
+ unsigned int inserted: 1; /* set when newly inserted */
+ struct s_tree_node *parent;
+ struct s_tree_node *next; /* next hash of FileIndex */
+
+ /* The above ^^^ must be identical to a TREE_NODE structure */
+ struct s_tree_node *first; /* first entry in the tree */
+ struct s_tree_node *last; /* last entry in tree */
+ struct s_mem *mem; /* tree memory */
+ uint32_t total_size; /* total bytes allocated */
+ uint32_t blocks; /* total mallocs */
+ int cached_path_len; /* length of cached path */
+ char *cached_path; /* cached current path */
+ TREE_NODE *cached_parent; /* cached parent for above path */
+};
+typedef struct s_tree_root TREE_ROOT;
+
+#else
#define foreach_child(cld, node) \
- for(cld=(node)->child_; cld; cld=cld->sibling_)
+ for(cld=(node)->child_; cld; cld=cld->sibling_)
#define tree_node_has_child(node) \
- ((node)->child_ != NULL)
+ ((node)->child_ != NULL)
#define first_child(node) \
- ((node)->child_)
+ ((node)->child_)
/*
* there is one for each file.
*/
struct s_tree_node {
- char *fname; /* file name */
- int32_t FileIndex; /* file index */
- uint32_t JobId; /* JobId */
- uint16_t fname_len; /* filename length */
- int type: 8; /* node type */
- unsigned int extract: 1; /* extract item */
+ char *fname; /* file name */
+ int32_t FileIndex; /* file index */
+ uint32_t JobId; /* JobId */
+ uint16_t fname_len; /* filename length */
+ int type: 8; /* node type */
+ unsigned int extract: 1; /* extract item */
unsigned int extract_dir: 1; /* extract dir entry only */
- unsigned int hard_link: 1; /* set if have hard link */
- unsigned int soft_link: 1; /* set if is soft link */
- unsigned int inserted: 1; /* set when newly inserted */
+ unsigned int hard_link: 1; /* set if have hard link */
+ unsigned int soft_link: 1; /* set if is soft link */
+ unsigned int inserted: 1; /* set when newly inserted */
struct s_tree_node *parent;
struct s_tree_node *sibling_;
- struct s_tree_node *next; /* next hash of FileIndex */
+ struct s_tree_node *next; /* next hash of FileIndex */
struct s_tree_node *child_;
};
typedef struct s_tree_node TREE_NODE;
struct s_tree_root {
- char *fname; /* file name */
- int32_t FileIndex; /* file index */
- uint32_t JobId; /* JobId */
- uint16_t fname_len; /* filename length */
- unsigned int type: 8; /* node type */
- unsigned int extract: 1; /* extract item */
+ char *fname; /* file name */
+ int32_t FileIndex; /* file index */
+ uint32_t JobId; /* JobId */
+ uint16_t fname_len; /* filename length */
+ unsigned int type: 8; /* node type */
+ unsigned int extract: 1; /* extract item */
unsigned int extract_dir: 1; /* extract dir entry only */
- unsigned int have_link: 1; /* set if have hard link */
- unsigned int inserted: 1; /* set when newly inserted */
+ unsigned int have_link: 1; /* set if have hard link */
+ unsigned int inserted: 1; /* set when newly inserted */
struct s_tree_node *parent;
struct s_tree_node *sibling_;
- struct s_tree_node *next; /* next hash of FileIndex */
+ struct s_tree_node *next; /* next hash of FileIndex */
struct s_tree_node *child_;
/* The above ^^^ must be identical to a TREE_NODE structure */
- struct s_tree_node *first; /* first entry in the tree */
- struct s_tree_node *last; /* last entry in tree */
- struct s_mem *mem; /* tree memory */
- uint32_t total_size; /* total bytes allocated */
- uint32_t blocks; /* total mallocs */
- int cached_path_len; /* length of cached path */
- char *cached_path; /* cached current path */
- TREE_NODE *cached_parent; /* cached parent for above path */
+ struct s_tree_node *first; /* first entry in the tree */
+ struct s_tree_node *last; /* last entry in tree */
+ struct s_mem *mem; /* tree memory */
+ uint32_t total_size; /* total bytes allocated */
+ uint32_t blocks; /* total mallocs */
+ int cached_path_len; /* length of cached path */
+ char *cached_path; /* cached current path */
+ TREE_NODE *cached_parent; /* cached parent for above path */
};
typedef struct s_tree_root TREE_ROOT;
+#endif
/* type values */
-#define TN_ROOT 1 /* root node */
-#define TN_NEWDIR 2 /* created directory to fill path */
-#define TN_DIR 3 /* directory entry */
-#define TN_DIR_NLS 4 /* directory -- no leading slash -- win32 */
-#define TN_FILE 5 /* file entry */
+#define TN_ROOT 1 /* root node */
+#define TN_NEWDIR 2 /* created directory to fill path */
+#define TN_DIR 3 /* directory entry */
+#define TN_DIR_NLS 4 /* directory -- no leading slash -- win32 */
+#define TN_FILE 5 /* file entry */
/* External interface */
TREE_ROOT *new_tree(int count);
-TREE_NODE *insert_tree_node(char *path, char *fname, TREE_NODE *node,
- TREE_ROOT *root, TREE_NODE *parent);
+TREE_NODE *insert_tree_node(char *path, char *fname, int type,
+ TREE_ROOT *root, TREE_NODE *parent);
TREE_NODE *make_tree_path(char *path, TREE_ROOT *root);
TREE_NODE *tree_cwd(char *path, TREE_ROOT *root, TREE_NODE *node);
TREE_NODE *tree_relcwd(char *path, TREE_ROOT *root, TREE_NODE *node);
void free_tree(TREE_ROOT *root);
int tree_getpath(TREE_NODE *node, char *buf, int buf_size);
+/*
+ * Use the following for traversing the whole tree. It will be
+ * traversed in the order the entries were inserted into the
+ * tree.
+ */
#ifdef SLOW_WAY
TREE_NODE *first_tree_node(TREE_ROOT *root);
TREE_NODE *next_tree_node(TREE_NODE *node);
#undef VERSION
#define VERSION "1.35.0"
#define VSTRING "1"
-#define BDATE "16 June 2004"
-#define LSMDATE "16Jun04"
+#define BDATE "18 June 2004"
+#define LSMDATE "18Jun04"
/* Debug flags */
#undef DEBUG