]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bcopy.c
Make 5 min Heartbeat the default
[bacula/bacula] / bacula / src / stored / bcopy.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2014 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from many
7    others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    Bacula® is a registered trademark of Kern Sibbald.
15 */
16 /*
17  *
18  *  Program to copy a Bacula from one volume to another.
19  *
20  *   Written by Kern E. Sibbald, October 2002
21  *
22  */
23
24 #include "bacula.h"
25 #include "stored.h"
26
27 extern bool parse_sd_config(CONFIG *config, const char *configfile, int exit_code);
28
29 /* Forward referenced functions */
30 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec);
31 static bool record_cb(DCR *dcr, DEV_RECORD *rec);
32
33
34 /* Global variables */
35 static DEVICE *in_dev = NULL;
36 static DEVICE *out_dev = NULL;
37 static JCR *in_jcr;                    /* input jcr */
38 static JCR *out_jcr;                   /* output jcr */
39 static BSR *bsr = NULL;
40 static const char *wd = "/tmp";
41 static bool list_records = false;
42 static uint32_t records = 0;
43 static uint32_t jobs = 0;
44 static DEV_BLOCK *out_block;
45 static SESSION_LABEL sessrec;
46
47 static CONFIG *config;
48 #define CONFIG_FILE "bacula-sd.conf"
49
50 void *start_heap;
51 char *configfile = NULL;
52 STORES *me = NULL;                    /* our Global resource */
53 bool forge_on = false;                /* proceed inspite of I/O errors */
54 pthread_mutex_t device_release_mutex = PTHREAD_MUTEX_INITIALIZER;
55 pthread_cond_t wait_device_release = PTHREAD_COND_INITIALIZER;
56
57
58 static void usage()
59 {
60    fprintf(stderr, _(
61 PROG_COPYRIGHT
62 "\nVersion: %s (%s)\n\n"
63 "Usage: bcopy [-d debug_level] <input-archive> <output-archive>\n"
64 "       -b bootstrap      specify a bootstrap file\n"
65 "       -c <file>         specify a Storage configuration file\n"
66 "       -d <nn>           set debug level to <nn>\n"
67 "       -dt               print timestamp in debug output\n"
68 "       -i                specify input Volume names (separated by |)\n"
69 "       -o                specify output Volume names (separated by |)\n"
70 "       -p                proceed inspite of errors\n"
71 "       -v                verbose\n"
72 "       -w <dir>          specify working directory (default /tmp)\n"
73 "       -?                print this message\n\n"), 2002, VERSION, BDATE);
74    exit(1);
75 }
76
77 int main (int argc, char *argv[])
78 {
79    int ch;
80    char *iVolumeName = NULL;
81    char *oVolumeName = NULL;
82    bool ignore_label_errors = false;
83    bool ok;
84
85    setlocale(LC_ALL, "");
86    bindtextdomain("bacula", LOCALEDIR);
87    textdomain("bacula");
88    init_stack_dump();
89
90    my_name_is(argc, argv, "bcopy");
91    lmgr_init_thread();
92    init_msg(NULL, NULL);
93
94    while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
95       switch (ch) {
96       case 'b':
97          bsr = parse_bsr(NULL, optarg);
98          break;
99
100       case 'c':                    /* specify config file */
101          if (configfile != NULL) {
102             free(configfile);
103          }
104          configfile = bstrdup(optarg);
105          break;
106
107       case 'd':                    /* debug level */
108          if (*optarg == 't') {
109             dbg_timestamp = true;
110          } else {
111             debug_level = atoi(optarg);
112             if (debug_level <= 0) {
113                debug_level = 1;
114             }
115          }
116          break;
117
118       case 'i':                    /* input Volume name */
119          iVolumeName = optarg;
120          break;
121
122       case 'o':                    /* output Volume name */
123          oVolumeName = optarg;
124          break;
125
126       case 'p':
127          ignore_label_errors = true;
128          forge_on = true;
129          break;
130
131       case 'v':
132          verbose++;
133          break;
134
135       case 'w':
136          wd = optarg;
137          break;
138
139       case '?':
140       default:
141          usage();
142
143       }
144    }
145    argc -= optind;
146    argv += optind;
147
148    if (argc != 2) {
149       Pmsg0(0, _("Wrong number of arguments: \n"));
150       usage();
151    }
152
153    OSDependentInit();
154
155    working_directory = wd;
156
157    if (configfile == NULL) {
158       configfile = bstrdup(CONFIG_FILE);
159    }
160
161    config = new_config_parser();
162    parse_sd_config(config, configfile, M_ERROR_TERM);
163    setup_me();
164    load_sd_plugins(me->plugin_directory);
165
166    /* Setup and acquire input device for reading */
167    Dmsg0(100, "About to setup input jcr\n");
168    in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName, SD_READ); /* read device */
169    if (!in_jcr) {
170       exit(1);
171    }
172    in_jcr->ignore_label_errors = ignore_label_errors;
173    in_dev = in_jcr->dcr->dev;
174    if (!in_dev) {
175       exit(1);
176    }
177
178    /* Setup output device for writing */
179    Dmsg0(100, "About to setup output jcr\n");
180    out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName, SD_APPEND); /* no acquire */
181    if (!out_jcr) {
182       exit(1);
183    }
184    out_dev = out_jcr->dcr->dev;
185    if (!out_dev) {
186       exit(1);
187    }
188    Dmsg0(100, "About to acquire device for writing\n");
189    /* For we must now acquire the device for writing */
190    out_dev->rLock(false);
191    if (!out_dev->open(out_jcr->dcr, OPEN_READ_WRITE)) {
192       Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), out_dev->errmsg);
193       out_dev->Unlock();
194       exit(1);
195    }
196    out_dev->Unlock();
197    if (!acquire_device_for_append(out_jcr->dcr)) {
198       free_jcr(in_jcr);
199       exit(1);
200    }
201    out_block = out_jcr->dcr->block;
202
203    ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
204
205    if (ok || out_dev->can_write()) {
206       if (!out_jcr->dcr->write_block_to_device()) {
207          Pmsg0(000, _("Write of last block failed.\n"));
208       }
209    }
210
211    Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
212
213    free_jcr(in_jcr);
214    free_jcr(out_jcr);
215
216    in_dev->term();
217    out_dev->term();
218    return 0;
219 }
220
221
222 /*
223  * read_records() calls back here for each record it gets
224  */
225 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
226 {
227    if (list_records) {
228       Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
229             rec->VolSessionId, rec->VolSessionTime, rec->FileIndex,
230             rec->Stream, rec->data_len);
231    }
232    /*
233     * Check for Start or End of Session Record
234     *
235     */
236    if (rec->FileIndex < 0) {
237       get_session_record(in_dcr->dev, rec, &sessrec);
238
239       if (verbose > 1) {
240          dump_label_record(in_dcr->dev, rec, 1);
241       }
242       switch (rec->FileIndex) {
243       case PRE_LABEL:
244          Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
245          return false;
246       case VOL_LABEL:
247          Pmsg0(000, _("Volume label not copied.\n"));
248          return true;
249       case SOS_LABEL:
250          if (bsr && rec->match_stat < 1) {
251             /* Skipping record, because does not match BSR filter */
252             if (verbose) {
253              Pmsg0(-1, _("Copy skipped. Record does not match BSR filter.\n"));
254             }
255          } else {
256             jobs++;
257          }
258          break;
259       case EOS_LABEL:
260          if (bsr && rec->match_stat < 1) {
261             /* Skipping record, because does not match BSR filter */
262            return true;
263         }
264          while (!write_record_to_block(out_jcr->dcr, rec)) {
265             Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
266                        rec->remainder);
267             if (!out_jcr->dcr->write_block_to_device()) {
268                Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
269                   out_dev->print_name(), out_dev->bstrerror());
270                Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
271                      out_dev->bstrerror());
272                return false;
273             }
274          }
275          if (!out_jcr->dcr->write_block_to_device()) {
276             Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
277                out_dev->print_name(), out_dev->bstrerror());
278             Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
279                   out_dev->bstrerror());
280             return false;
281          }
282          return true;
283       case EOM_LABEL:
284          Pmsg0(000, _("EOM label not copied.\n"));
285          return true;
286       case EOT_LABEL:              /* end of all tapes */
287          Pmsg0(000, _("EOT label not copied.\n"));
288          return true;
289       default:
290          return true;
291       }
292    }
293
294    /*  Write record */
295    if (bsr && rec->match_stat < 1) {
296       /* Skipping record, because does not match BSR filter */
297       return true;
298    }
299    records++;
300    while (!write_record_to_block(out_jcr->dcr, rec)) {
301       Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
302                  rec->remainder);
303       if (!out_jcr->dcr->write_block_to_device()) {
304          Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
305             out_dev->print_name(), out_dev->bstrerror());
306          Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
307                out_dev->bstrerror());
308          return false;
309       }
310    }
311    return true;
312 }
313
314 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
315 {
316    const char *rtype;
317    memset(sessrec, 0, sizeof(SESSION_LABEL));
318    switch (rec->FileIndex) {
319    case PRE_LABEL:
320       rtype = _("Fresh Volume Label");
321       break;
322    case VOL_LABEL:
323       rtype = _("Volume Label");
324       unser_volume_label(dev, rec);
325       break;
326    case SOS_LABEL:
327       rtype = _("Begin Job Session");
328       unser_session_label(sessrec, rec);
329       break;
330    case EOS_LABEL:
331       rtype = _("End Job Session");
332       unser_session_label(sessrec, rec);
333       break;
334    case 0:
335    case EOM_LABEL:
336       rtype = _("End of Medium");
337       break;
338    default:
339       rtype = _("Unknown");
340       break;
341    }
342    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
343          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
344    if (verbose) {
345       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
346             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
347    }
348 }
349
350
351 /* Dummies to replace askdir.c */
352 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
353 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
354 bool    dir_create_jobmedia_record(DCR *dcr, bool zero) { return 1; }
355 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
356 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
357 bool    dir_send_job_status(JCR *jcr) {return 1;}
358
359
360 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
361 {
362    DEVICE *dev = dcr->dev;
363    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
364       dcr->VolumeName, dev->print_name());
365    dev->close();
366    getchar();
367    return true;
368 }
369
370 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
371 {
372    Dmsg0(100, "Fake dir_get_volume_info\n");
373    dcr->setVolCatName(dcr->VolumeName);
374 #ifdef BUILD_DVD
375    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
376 #endif
377    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatParts);
378    return 1;
379 }