2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of Kern Sibbald.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Program to copy a Bacula from one volume to another.
32 * Kern E. Sibbald, October 2002
42 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
43 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
45 /* Forward referenced functions */
46 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
47 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
50 /* Global variables */
51 static DEVICE *in_dev = NULL;
52 static DEVICE *out_dev = NULL;
53 static JCR *in_jcr; /* input jcr */
54 static JCR *out_jcr; /* output jcr */
55 static BSR *bsr = NULL;
56 static const char *wd = "/tmp";
57 static bool list_records = false;
58 static uint32_t records = 0;
59 static uint32_t jobs = 0;
60 static DEV_BLOCK *out_block;
61 static SESSION_LABEL sessrec;
63 static CONFIG *config;
64 #define CONFIG_FILE "bacula-sd.conf"
65 char *configfile = NULL;
66 STORES *me = NULL; /* our Global resource */
67 bool forge_on = false; /* proceed inspite of I/O errors */
68 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
69 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
76 "\nVersion: %s (%s)\n\n"
77 "Usage: bcopy [-d debug_level] <input-archive> <output-archive>\n"
78 " -b bootstrap specify a bootstrap file\n"
79 " -c <file> specify a Storage configuration file\n"
80 " -d <nn> set debug level to <nn>\n"
81 " -dt print timestamp in debug output\n"
82 " -i specify input Volume names (separated by |)\n"
83 " -o specify output Volume names (separated by |)\n"
84 " -p proceed inspite of errors\n"
86 " -w <dir> specify working directory (default /tmp)\n"
87 " -? print this message\n\n"), 2002, VERSION, BDATE);
91 int main (int argc, char *argv[])
94 char *iVolumeName = NULL;
95 char *oVolumeName = NULL;
96 bool ignore_label_errors = false;
99 setlocale(LC_ALL, "");
100 bindtextdomain("bacula", LOCALEDIR);
101 textdomain("bacula");
104 my_name_is(argc, argv, "bcopy");
105 init_msg(NULL, NULL);
107 while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
110 bsr = parse_bsr(NULL, optarg);
113 case 'c': /* specify config file */
114 if (configfile != NULL) {
117 configfile = bstrdup(optarg);
120 case 'd': /* debug level */
121 if (*optarg == 't') {
122 dbg_timestamp = true;
124 debug_level = atoi(optarg);
125 if (debug_level <= 0) {
131 case 'i': /* input Volume name */
132 iVolumeName = optarg;
135 case 'o': /* output Volume name */
136 oVolumeName = optarg;
140 ignore_label_errors = true;
162 Pmsg0(0, _("Wrong number of arguments: \n"));
168 working_directory = wd;
170 if (configfile == NULL) {
171 configfile = bstrdup(CONFIG_FILE);
174 config = new_config_parser();
175 parse_sd_config(config, configfile, M_ERROR_TERM);
177 /* Setup and acquire input device for reading */
178 Dmsg0(100, "About to setup input jcr\n");
179 in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, 1); /* read device */
183 in_jcr->ignore_label_errors = ignore_label_errors;
184 in_dev = in_jcr->dcr->dev;
189 /* Setup output device for writing */
190 Dmsg0(100, "About to setup output jcr\n");
191 out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, 0); /* no acquire */
195 out_dev = out_jcr->dcr->dev;
199 Dmsg0(100, "About to acquire device for writing\n");
200 /* For we must now acquire the device for writing */
202 if (out_dev->open(out_jcr->dcr, OPEN_READ_WRITE) < 0) {
203 Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
208 if (!acquire_device_for_append(out_jcr->dcr)) {
212 out_block = out_jcr->dcr->block;
214 ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
216 if (ok || out_dev->can_write()) {
217 if (!write_block_to_device(out_jcr->dcr)) {
218 Pmsg0(000, _("Write of last block failed.\n"));
222 Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
234 * read_records() calls back here for each record it gets
236 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
239 Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
240 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
241 rec->Stream, rec->data_len);
244 * Check for Start or End of Session Record
247 if (rec->FileIndex < 0) {
248 get_session_record(in_dcr->dev, rec, &sessrec);
251 dump_label_record(in_dcr->dev, rec, 1);
253 switch (rec->FileIndex) {
255 Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
258 Pmsg0(000, _("Volume label not copied.\n"));
261 if (bsr && rec->match_stat < 1) {
262 /* Skipping record, because does not match BSR filter */
264 Pmsg0(-1, _("Copy skipped. Record does not match BSR filter.\n"));
271 if (bsr && rec->match_stat < 1) {
272 /* Skipping record, because does not match BSR filter */
275 while (!write_record_to_block(out_block, rec)) {
276 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
278 if (!write_block_to_device(out_jcr->dcr)) {
279 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
280 out_dev->print_name(), out_dev->bstrerror());
281 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
282 out_dev->bstrerror());
286 if (!write_block_to_device(out_jcr->dcr)) {
287 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
288 out_dev->print_name(), out_dev->bstrerror());
289 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
290 out_dev->bstrerror());
295 Pmsg0(000, _("EOM label not copied.\n"));
297 case EOT_LABEL: /* end of all tapes */
298 Pmsg0(000, _("EOT label not copied.\n"));
306 if (bsr && rec->match_stat < 1) {
307 /* Skipping record, because does not match BSR filter */
311 while (!write_record_to_block(out_block, rec)) {
312 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
314 if (!write_block_to_device(out_jcr->dcr)) {
315 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
316 out_dev->print_name(), out_dev->bstrerror());
317 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
318 out_dev->bstrerror());
325 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
328 memset(sessrec, 0, sizeof(sessrec));
329 switch (rec->FileIndex) {
331 rtype = _("Fresh Volume Label");
334 rtype = _("Volume Label");
335 unser_volume_label(dev, rec);
338 rtype = _("Begin Job Session");
339 unser_session_label(sessrec, rec);
342 rtype = _("End Job Session");
343 unser_session_label(sessrec, rec);
347 rtype = _("End of Medium");
350 rtype = _("Unknown");
353 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
354 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
356 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
357 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
362 /* Dummies to replace askdir.c */
363 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
364 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
365 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
366 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
367 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
368 bool dir_send_job_status(JCR *jcr) {return 1;}
371 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
373 DEVICE *dev = dcr->dev;
374 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
375 dcr->VolumeName, dev->print_name());
381 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
383 Dmsg0(100, "Fake dir_get_volume_info\n");
384 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
385 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
386 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);