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; }
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 " -i specify input Volume names (separated by |)\n"
80 " -o specify output Volume names (separated by |)\n"
81 " -p proceed inspite of errors\n"
83 " -w <dir> specify working directory (default /tmp)\n"
84 " -? print this message\n\n"), 2002, VERSION, BDATE);
88 int main (int argc, char *argv[])
91 char *iVolumeName = NULL;
92 char *oVolumeName = NULL;
93 bool ignore_label_errors = false;
96 setlocale(LC_ALL, "");
97 bindtextdomain("bacula", LOCALEDIR);
101 my_name_is(argc, argv, "bcopy");
102 init_msg(NULL, NULL);
104 while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
107 bsr = parse_bsr(NULL, optarg);
110 case 'c': /* specify config file */
111 if (configfile != NULL) {
114 configfile = bstrdup(optarg);
117 case 'd': /* debug level */
118 if (*optarg == 't') {
119 dbg_timestamp = true;
121 debug_level = atoi(optarg);
122 if (debug_level <= 0) {
128 case 'i': /* input Volume name */
129 iVolumeName = optarg;
132 case 'o': /* output Volume name */
133 oVolumeName = optarg;
137 ignore_label_errors = true;
159 Pmsg0(0, _("Wrong number of arguments: \n"));
165 working_directory = wd;
167 if (configfile == NULL) {
168 configfile = bstrdup(CONFIG_FILE);
171 parse_config(configfile);
173 /* Setup and acquire input device for reading */
174 Dmsg0(100, "About to setup input jcr\n");
175 in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, 1); /* read device */
179 in_jcr->ignore_label_errors = ignore_label_errors;
180 in_dev = in_jcr->dcr->dev;
185 /* Setup output device for writing */
186 Dmsg0(100, "About to setup output jcr\n");
187 out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, 0); /* no acquire */
191 out_dev = out_jcr->dcr->dev;
195 Dmsg0(100, "About to acquire device for writing\n");
196 /* For we must now acquire the device for writing */
198 if (out_dev->open(out_jcr->dcr, OPEN_READ_WRITE) < 0) {
199 Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
204 if (!acquire_device_for_append(out_jcr->dcr)) {
208 out_block = out_jcr->dcr->block;
210 ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
212 if (ok || out_dev->can_write()) {
213 if (!write_block_to_device(out_jcr->dcr)) {
214 Pmsg0(000, _("Write of last block failed.\n"));
218 Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
230 * read_records() calls back here for each record it gets
232 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
235 Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
236 rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
237 rec->Stream, rec->data_len);
240 * Check for Start or End of Session Record
243 if (rec->FileIndex < 0) {
244 get_session_record(in_dcr->dev, rec, &sessrec);
247 dump_label_record(in_dcr->dev, rec, 1);
249 switch (rec->FileIndex) {
251 Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
254 Pmsg0(000, _("Volume label not copied.\n"));
257 if (bsr && rec->match_stat < 1) {
258 /* Skipping record, because does not match BSR filter */
260 Pmsg0(-1, _("Copy skipped. Record does not match BSR filter.\n"));
267 if (bsr && rec->match_stat < 1) {
268 /* Skipping record, because does not match BSR filter */
271 while (!write_record_to_block(out_block, rec)) {
272 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
274 if (!write_block_to_device(out_jcr->dcr)) {
275 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
276 out_dev->print_name(), out_dev->bstrerror());
277 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
278 out_dev->bstrerror());
282 if (!write_block_to_device(out_jcr->dcr)) {
283 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
284 out_dev->print_name(), out_dev->bstrerror());
285 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
286 out_dev->bstrerror());
291 Pmsg0(000, _("EOM label not copied.\n"));
293 case EOT_LABEL: /* end of all tapes */
294 Pmsg0(000, _("EOT label not copied.\n"));
302 if (bsr && rec->match_stat < 1) {
303 /* Skipping record, because does not match BSR filter */
307 while (!write_record_to_block(out_block, rec)) {
308 Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
310 if (!write_block_to_device(out_jcr->dcr)) {
311 Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
312 out_dev->print_name(), out_dev->bstrerror());
313 Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
314 out_dev->bstrerror());
321 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
324 memset(sessrec, 0, sizeof(sessrec));
325 switch (rec->FileIndex) {
327 rtype = _("Fresh Volume Label");
330 rtype = _("Volume Label");
331 unser_volume_label(dev, rec);
334 rtype = _("Begin Job Session");
335 unser_session_label(sessrec, rec);
338 rtype = _("End Job Session");
339 unser_session_label(sessrec, rec);
343 rtype = _("End of Medium");
346 rtype = _("Unknown");
349 Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
350 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
352 Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
353 rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
358 /* Dummies to replace askdir.c */
359 bool dir_find_next_appendable_volume(DCR *dcr) { return 1;}
360 bool dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
361 bool dir_create_jobmedia_record(DCR *dcr) { return 1; }
362 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
363 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
364 bool dir_send_job_status(JCR *jcr) {return 1;}
367 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
369 DEVICE *dev = dcr->dev;
370 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
371 dcr->VolumeName, dev->print_name());
377 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
379 Dmsg0(100, "Fake dir_get_volume_info\n");
380 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
381 dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
382 Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);