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 John Walker.
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; }
44 /* Forward referenced functions */
45 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
46 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
49 /* Global variables */
50 static DEVICE *in_dev = NULL;
51 static DEVICE *out_dev = NULL;
52 static JCR *in_jcr; /* input jcr */
53 static JCR *out_jcr; /* output jcr */
54 static BSR *bsr = NULL;
55 static const char *wd = "/tmp";
56 static bool list_records = false;
57 static uint32_t records = 0;
58 static uint32_t jobs = 0;
59 static DEV_BLOCK *out_block;
60 static SESSION_LABEL sessrec;
62 #define CONFIG_FILE "bacula-sd.conf"
63 char *configfile = NULL;
64 STORES *me = NULL; /* our Global resource */
65 bool forge_on = false; /* proceed inspite of I/O errors */
66 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
67 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
74 "\nVersion: %s (%s)\n\n"
75 "Usage: bcopy [-d debug_level] <input-archive> <output-archive>\n"
76 " -b bootstrap specify a bootstrap file\n"
77 " -c <file> specify configuration file\n"
78 " -d <nn> set debug level to <nn>\n"
79 " -dt print timestamp in debug output\n"
80 " -i specify input Volume names (separated by |)\n"
81 " -o specify output Volume names (separated by |)\n"
82 " -p proceed inspite of errors\n"
84 " -w <dir> specify working directory (default /tmp)\n"
85 " -? print this message\n\n"), 2002, VERSION, BDATE);
89 int main (int argc, char *argv[])
92 char *iVolumeName = NULL;
93 char *oVolumeName = NULL;
94 bool ignore_label_errors = false;
97 setlocale(LC_ALL, "");
98 bindtextdomain("bacula", LOCALEDIR);
102 my_name_is(argc, argv, "bcopy");
103 init_msg(NULL, NULL);
105 while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
108 bsr = parse_bsr(NULL, optarg);
111 case 'c': /* specify config file */
112 if (configfile != NULL) {
115 configfile = bstrdup(optarg);
118 case 'd': /* debug level */
119 if (*optarg == 't') {
120 dbg_timestamp = true;
122 debug_level = atoi(optarg);
123 if (debug_level <= 0) {
129 case 'i': /* input Volume name */
130 iVolumeName = optarg;
133 case 'o': /* output Volume name */
134 oVolumeName = optarg;
138 ignore_label_errors = true;
160 Pmsg0(0, _("Wrong number of arguments: \n"));
166 working_directory = wd;
168 if (configfile == NULL) {
169 configfile = bstrdup(CONFIG_FILE);
172 parse_config(configfile);
174 /* Setup and acquire input device for reading */
175 Dmsg0(100, "About to setup input jcr\n");
176 in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, 1); /* read device */
180 in_jcr->ignore_label_errors = ignore_label_errors;
181 in_dev = in_jcr->dcr->dev;
186 /* Setup output device for writing */
187 Dmsg0(100, "About to setup output jcr\n");
188 out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, 0); /* no acquire */
192 out_dev = out_jcr->dcr->dev;
196 Dmsg0(100, "About to acquire device for writing\n");
197 /* For we must now acquire the device for writing */
199 if (out_dev->open(out_jcr->dcr, OPEN_READ_WRITE) < 0) {
200 Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
205 if (!acquire_device_for_append(out_jcr->dcr)) {
209 out_block = out_jcr->dcr->block;
211 ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
213 if (ok || out_dev->can_write()) {
214 if (!write_block_to_device(out_jcr->dcr)) {
215 Pmsg0(000, _("Write of last block failed.\n"));
219 Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
231 * read_records() calls back here for each record it gets
233 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
236 Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
237 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
238 rec->Stream, rec->data_len);
241 * Check for Start or End of Session Record
244 if (rec->FileIndex < 0) {
245 get_session_record(in_dcr->dev, rec, &sessrec);
248 dump_label_record(in_dcr->dev, rec, 1);
250 switch (rec->FileIndex) {
252 Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
255 Pmsg0(000, _("Volume label not copied.\n"));
261 while (!write_record_to_block(out_block, rec)) {
262 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
264 if (!write_block_to_device(out_jcr->dcr)) {
265 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
266 out_dev->print_name(), out_dev->bstrerror());
267 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
268 out_dev->bstrerror());
272 if (!write_block_to_device(out_jcr->dcr)) {
273 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
274 out_dev->print_name(), out_dev->bstrerror());
275 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
276 out_dev->bstrerror());
281 Pmsg0(000, _("EOM label not copied.\n"));
283 case EOT_LABEL: /* end of all tapes */
284 Pmsg0(000, _("EOT label not copied.\n"));
293 while (!write_record_to_block(out_block, rec)) {
294 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
296 if (!write_block_to_device(out_jcr->dcr)) {
297 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
298 out_dev->print_name(), out_dev->bstrerror());
299 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
300 out_dev->bstrerror());
307 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
310 memset(sessrec, 0, sizeof(sessrec));
311 switch (rec->FileIndex) {
313 rtype = _("Fresh Volume Label");
316 rtype = _("Volume Label");
317 unser_volume_label(dev, rec);
320 rtype = _("Begin Job Session");
321 unser_session_label(sessrec, rec);
324 rtype = _("End Job Session");
325 unser_session_label(sessrec, rec);
329 rtype = _("End of Medium");
332 rtype = _("Unknown");
335 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
336 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
338 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
339 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
344 /* Dummies to replace askdir.c */
345 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
346 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
347 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
348 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
349 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
350 bool dir_send_job_status(JCR *jcr) {return 1;}
353 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
355 DEVICE *dev = dcr->dev;
356 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
357 dcr->VolumeName, dev->print_name());
363 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
365 Dmsg0(100, "Fake dir_get_volume_info\n");
366 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
367 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
368 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);