]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/bcopy.c
33a60d7bb00637a40dffa5dfce49e4de2d2f1302
[bacula/bacula] / bacula / src / stored / bcopy.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2002-2008 Free Software Foundation Europe e.V.
5
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
11    in the file LICENSE.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28 /*
29  *
30  *  Program to copy a Bacula from one volume to another.
31  *
32  *   Kern E. Sibbald, October 2002
33  *
34  *
35  *   Version $Id$
36  */
37
38 #include "bacula.h"
39 #include "stored.h"
40
41 /* Dummy functions */
42 int generate_daemon_event(JCR *jcr, const char *event) { return 1; }
43
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);
47
48
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;
61
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;
68
69
70 static void usage()
71 {
72    fprintf(stderr, _(
73 PROG_COPYRIGHT
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"
82 "       -v                verbose\n"
83 "       -w <dir>          specify working directory (default /tmp)\n"
84 "       -?                print this message\n\n"), 2002, VERSION, BDATE);
85    exit(1);
86 }
87
88 int main (int argc, char *argv[])
89 {
90    int ch;
91    char *iVolumeName = NULL;
92    char *oVolumeName = NULL;
93    bool ignore_label_errors = false;
94    bool ok;
95
96    setlocale(LC_ALL, "");
97    bindtextdomain("bacula", LOCALEDIR);
98    textdomain("bacula");
99    init_stack_dump();
100
101    my_name_is(argc, argv, "bcopy");
102    init_msg(NULL, NULL);
103
104    while ((ch = getopt(argc, argv, "b:c:d:i:o:pvw:?")) != -1) {
105       switch (ch) {
106       case 'b':
107          bsr = parse_bsr(NULL, optarg);
108          break;
109
110       case 'c':                    /* specify config file */
111          if (configfile != NULL) {
112             free(configfile);
113          }
114          configfile = bstrdup(optarg);
115          break;
116
117       case 'd':                    /* debug level */
118          if (*optarg == 't') {
119             dbg_timestamp = true;
120          } else {
121             debug_level = atoi(optarg);
122             if (debug_level <= 0) {
123                debug_level = 1;
124             }
125          }
126          break;
127
128       case 'i':                    /* input Volume name */
129          iVolumeName = optarg;
130          break;
131
132       case 'o':                    /* output Volume name */
133          oVolumeName = optarg;
134          break;
135
136       case 'p':
137          ignore_label_errors = true;
138          forge_on = true;
139          break;
140
141       case 'v':
142          verbose++;
143          break;
144
145       case 'w':
146          wd = optarg;
147          break;
148
149       case '?':
150       default:
151          usage();
152
153       }
154    }
155    argc -= optind;
156    argv += optind;
157
158    if (argc != 2) {
159       Pmsg0(0, _("Wrong number of arguments: \n"));
160       usage();
161    }
162
163    OSDependentInit();
164
165    working_directory = wd;
166
167    if (configfile == NULL) {
168       configfile = bstrdup(CONFIG_FILE);
169    }
170
171    parse_config(configfile);
172
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 */
176    if (!in_jcr) {
177       exit(1);
178    }
179    in_jcr->ignore_label_errors = ignore_label_errors;
180    in_dev = in_jcr->dcr->dev;
181    if (!in_dev) {
182       exit(1);
183    }
184
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 */
188    if (!out_jcr) {
189       exit(1);
190    }
191    out_dev = out_jcr->dcr->dev;
192    if (!out_dev) {
193       exit(1);
194    }
195    Dmsg0(100, "About to acquire device for writing\n");
196    /* For we must now acquire the device for writing */
197    out_dev->r_dlock();
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);
200       out_dev->dunlock();
201       exit(1);
202    }
203    out_dev->dunlock();
204    if (!acquire_device_for_append(out_jcr->dcr)) {
205       free_jcr(in_jcr);
206       exit(1);
207    }
208    out_block = out_jcr->dcr->block;
209
210    ok = read_records(in_jcr->dcr, record_cb, mount_next_read_volume);
211
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"));
215       }
216    }
217
218    Pmsg2(000, _("%u Jobs copied. %u records copied.\n"), jobs, records);
219
220    free_jcr(in_jcr);
221    free_jcr(out_jcr);
222
223    in_dev->term();
224    out_dev->term();
225    return 0;
226 }
227
228
229 /*
230  * read_records() calls back here for each record it gets
231  */
232 static bool record_cb(DCR *in_dcr, DEV_RECORD *rec)
233 {
234    if (list_records) {
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);
238    }
239    /*
240     * Check for Start or End of Session Record
241     *
242     */
243    if (rec->FileIndex < 0) {
244       get_session_record(in_dcr->dev, rec, &sessrec);
245
246       if (verbose > 1) {
247          dump_label_record(in_dcr->dev, rec, 1);
248       }
249       switch (rec->FileIndex) {
250       case PRE_LABEL:
251          Pmsg0(000, _("Volume is prelabeled. This volume cannot be copied.\n"));
252          return false;
253       case VOL_LABEL:
254          Pmsg0(000, _("Volume label not copied.\n"));
255          return true;
256       case SOS_LABEL:
257          jobs++;
258          break;
259       case EOS_LABEL:
260          while (!write_record_to_block(out_block, rec)) {
261             Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
262                        rec->remainder);
263             if (!write_block_to_device(out_jcr->dcr)) {
264                Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
265                   out_dev->print_name(), out_dev->bstrerror());
266                Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
267                      out_dev->bstrerror());
268                return false;
269             }
270          }
271          if (!write_block_to_device(out_jcr->dcr)) {
272             Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
273                out_dev->print_name(), out_dev->bstrerror());
274             Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
275                   out_dev->bstrerror());
276             return false;
277          }
278          return true;
279       case EOM_LABEL:
280          Pmsg0(000, _("EOM label not copied.\n"));
281          return true;
282       case EOT_LABEL:              /* end of all tapes */
283          Pmsg0(000, _("EOT label not copied.\n"));
284          return true;
285       default:
286          return true;
287       }
288    }
289
290    /*  Write record */
291    records++;
292    while (!write_record_to_block(out_block, rec)) {
293       Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
294                  rec->remainder);
295       if (!write_block_to_device(out_jcr->dcr)) {
296          Dmsg2(90, "Got write_block_to_dev error on device %s: ERR=%s\n",
297             out_dev->print_name(), out_dev->bstrerror());
298          Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
299                out_dev->bstrerror());
300          return false;
301       }
302    }
303    return true;
304 }
305
306 static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sessrec)
307 {
308    const char *rtype;
309    memset(sessrec, 0, sizeof(sessrec));
310    switch (rec->FileIndex) {
311    case PRE_LABEL:
312       rtype = _("Fresh Volume Label");
313       break;
314    case VOL_LABEL:
315       rtype = _("Volume Label");
316       unser_volume_label(dev, rec);
317       break;
318    case SOS_LABEL:
319       rtype = _("Begin Job Session");
320       unser_session_label(sessrec, rec);
321       break;
322    case EOS_LABEL:
323       rtype = _("End Job Session");
324       unser_session_label(sessrec, rec);
325       break;
326    case 0:
327    case EOM_LABEL:
328       rtype = _("End of Medium");
329       break;
330    default:
331       rtype = _("Unknown");
332       break;
333    }
334    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
335          rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
336    if (verbose) {
337       Pmsg5(-1, _("%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n"),
338             rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
339    }
340 }
341
342
343 /* Dummies to replace askdir.c */
344 bool    dir_find_next_appendable_volume(DCR *dcr) { return 1;}
345 bool    dir_update_volume_info(DCR *dcr, bool relabel, bool update_LastWritten) { return 1; }
346 bool    dir_create_jobmedia_record(DCR *dcr) { return 1; }
347 bool    dir_ask_sysop_to_create_appendable_volume(DCR *dcr) { return 1; }
348 bool    dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec) { return 1;}
349 bool    dir_send_job_status(JCR *jcr) {return 1;}
350
351
352 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int /*mode*/)
353 {
354    DEVICE *dev = dcr->dev;
355    fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
356       dcr->VolumeName, dev->print_name());
357    dev->close();
358    getchar();
359    return true;
360 }
361
362 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw  writing)
363 {
364    Dmsg0(100, "Fake dir_get_volume_info\n");
365    bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
366    dcr->VolCatInfo.VolCatParts = find_num_dvd_parts(dcr);
367    Dmsg2(500, "Vol=%s num_parts=%d\n", dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolCatParts);
368    return 1;
369 }