2 Bacula® - The Network Backup Solution
4 Copyright (C) 2002-2010 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 three of the GNU Affero 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 Affero 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");
106 init_msg(NULL, NULL);
108 while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
111 bsr = parse_bsr(NULL, optarg);
114 case 'c': /* specify config file */
115 if (configfile != NULL) {
118 configfile = bstrdup(optarg);
121 case 'd': /* debug level */
122 if (*optarg == 't') {
123 dbg_timestamp = true;
125 debug_level = atoi(optarg);
126 if (debug_level <= 0) {
132 case 'i': /* input Volume name */
133 iVolumeName = optarg;
136 case 'o': /* output Volume name */
137 oVolumeName = optarg;
141 ignore_label_errors = true;
163 Pmsg0(0, _("Wrong number of arguments: \n"));
169 working_directory = wd;
171 if (configfile == NULL) {
172 configfile = bstrdup(CONFIG_FILE);
175 config = new_config_parser();
176 parse_sd_config(config, configfile, M_ERROR_TERM);
178 /* Setup and acquire input device for reading */
179 Dmsg0(100, "About to setup input jcr\n");
180 in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, 1); /* read device */
184 in_jcr->ignore_label_errors = ignore_label_errors;
185 in_dev = in_jcr->dcr->dev;
190 /* Setup output device for writing */
191 Dmsg0(100, "About to setup output jcr\n");
192 out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, 0); /* no acquire */
196 out_dev = out_jcr->dcr->dev;
200 Dmsg0(100, "About to acquire device for writing\n");
201 /* For we must now acquire the device for writing */
203 if (out_dev->open(out_jcr->dcr, OPEN_READ_WRITE) < 0) {
204 Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
209 if (!acquire_device_for_append(out_jcr->dcr)) {
213 out_block = out_jcr->dcr->block;
215 ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
217 if (ok || out_dev->can_write()) {
218 if (!write_block_to_device(out_jcr->dcr)) {
219 Pmsg0(000, _("Write of last block failed.\n"));
223 Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
235 * read_records() calls back here for each record it gets
237 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
240 Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
241 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
242 rec->Stream, rec->data_len);
245 * Check for Start or End of Session Record
248 if (rec->FileIndex < 0) {
249 get_session_record(in_dcr->dev, rec, &sessrec);
252 dump_label_record(in_dcr->dev, rec, 1);
254 switch (rec->FileIndex) {
256 Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
259 Pmsg0(000, _("Volume label not copied.\n"));
262 if (bsr && rec->match_stat < 1) {
263 /* Skipping record, because does not match BSR filter */
265 Pmsg0(-1, _("Copy skipped. Record does not match BSR filter.\n"));
272 if (bsr && rec->match_stat < 1) {
273 /* Skipping record, because does not match BSR filter */
276 while (!write_record_to_block(out_block, rec)) {
277 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
279 if (!write_block_to_device(out_jcr->dcr)) {
280 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
281 out_dev->print_name(), out_dev->bstrerror());
282 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
283 out_dev->bstrerror());
287 if (!write_block_to_device(out_jcr->dcr)) {
288 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
289 out_dev->print_name(), out_dev->bstrerror());
290 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
291 out_dev->bstrerror());
296 Pmsg0(000, _("EOM label not copied.\n"));
298 case EOT_LABEL: /* end of all tapes */
299 Pmsg0(000, _("EOT label not copied.\n"));
307 if (bsr && rec->match_stat < 1) {
308 /* Skipping record, because does not match BSR filter */
312 while (!write_record_to_block(out_block, rec)) {
313 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
315 if (!write_block_to_device(out_jcr->dcr)) {
316 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
317 out_dev->print_name(), out_dev->bstrerror());
318 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
319 out_dev->bstrerror());
326 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
329 memset(sessrec, 0, sizeof(sessrec));
330 switch (rec->FileIndex) {
332 rtype = _("Fresh Volume Label");
335 rtype = _("Volume Label");
336 unser_volume_label(dev, rec);
339 rtype = _("Begin Job Session");
340 unser_session_label(sessrec, rec);
343 rtype = _("End Job Session");
344 unser_session_label(sessrec, rec);
348 rtype = _("End of Medium");
351 rtype = _("Unknown");
354 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
355 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
357 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
358 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
363 /* Dummies to replace askdir.c */
364 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
365 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
366 bool dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
367 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
368 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
369 bool dir_send_job_status(JCR *jcr) {return 1;}
372 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
374 DEVICE *dev = dcr->dev;
375 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
376 dcr->VolumeName, dev->print_name());
382 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
384 Dmsg0(100, "Fake dir_get_volume_info\n");
385 dcr->setVolCatName(dcr->VolumeName);
386 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
387 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);