2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2012 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.
29 * Subroutines to handle Catalog reqests sent to the Director
30 * Reqests/commands from the Director are handled in dircmd.c
32 * Kern Sibbald, December 2000
36 #include "bacula.h" /* pull in global headers */
37 #include "stored.h" /* pull in Storage Deamon headers */
39 /* Requests sent to the Director */
40 static char Find_media[] = "CatReq Job=%s FindMedia=%d pool_name=%s media_type=%s\n";
41 static char Get_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s write=%d\n";
42 static char Update_media[] = "CatReq Job=%s UpdateMedia VolName=%s"
43 " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%s VolMounts=%u"
44 " VolErrors=%u VolWrites=%u MaxVolBytes=%s EndTime=%s VolStatus=%s"
45 " Slot=%d relabel=%d InChanger=%d VolReadTime=%s VolWriteTime=%s"
46 " VolFirstWritten=%s VolParts=%u\n";
47 static char Create_job_media[] = "CatReq Job=%s CreateJobMedia"
48 " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u"
49 " StartBlock=%u EndBlock=%u Copy=%d Strip=%d MediaId=%s\n";
50 static char FileAttributes[] = "UpdCat Job=%s FileAttributes ";
52 /* Responses received from the Director */
53 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
54 " VolBlocks=%lu VolBytes=%lld VolMounts=%lu VolErrors=%lu VolWrites=%lu"
55 " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
56 " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
57 " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
58 " VolParts=%lu LabelType=%ld MediaId=%lld\n";
61 static char OK_create[] = "1000 OK CreateJobMedia\n";
63 static pthread_mutex_t vol_info_mutex = PTHREAD_MUTEX_INITIALIZER;
67 static char Device_update[] = "DevUpd Job=%s device=%s "
68 "append=%d read=%d num_writers=%d "
69 "open=%d labeled=%d offline=%d "
70 "reserved=%d max_writers=%d "
71 "autoselect=%d autochanger=%d "
72 "changer_name=%s media_type=%s volume_name=%s\n";
75 /** Send update information about a device to Director */
76 bool dir_update_device(JCR *jcr, DEVICE *dev)
78 BSOCK *dir = jcr->dir_bsock;
79 POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
80 DEVRES *device = dev->device;
83 pm_strcpy(dev_name, device->hdr.name);
84 bash_spaces(dev_name);
85 if (dev->is_labeled()) {
86 pm_strcpy(VolumeName, dev->VolHdr.VolumeName);
88 pm_strcpy(VolumeName, "*");
90 bash_spaces(VolumeName);
91 pm_strcpy(MediaType, device->media_type);
92 bash_spaces(MediaType);
93 if (device->changer_res) {
94 pm_strcpy(ChangerName, device->changer_res->hdr.name);
95 bash_spaces(ChangerName);
97 pm_strcpy(ChangerName, "*");
99 ok = dir->fsend(Device_update,
102 dev->can_append()!=0,
103 dev->can_read()!=0, dev->num_writers,
104 dev->is_open()!=0, dev->is_labeled()!=0,
105 dev->is_offline()!=0, dev->reserved_device,
106 dev->is_tape()?100000:1,
108 ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
109 Dmsg1(100, ">dird: %s\n", dir->msg);
113 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
115 BSOCK *dir = jcr->dir_bsock;
116 POOL_MEM dev_name, MediaType;
120 pm_strcpy(dev_name, changer->hdr.name);
121 bash_spaces(dev_name);
122 device = (DEVRES *)changer->device->first();
123 pm_strcpy(MediaType, device->media_type);
124 bash_spaces(MediaType);
125 /* This is mostly to indicate that we are here */
126 ok = dir->fsend(Device_update,
128 dev_name.c_str(), /* Changer name */
129 0, 0, 0, /* append, read, num_writers */
130 0, 0, 0, /* is_open, is_labeled, offline */
131 0, 0, /* reserved, max_writers */
133 changer->device->size(), /* Number of devices */
135 "*", /* ChangerName */
136 MediaType.c_str(), /* MediaType */
138 Dmsg1(100, ">dird: %s\n", dir->msg);
145 * Send current JobStatus to Director
147 bool dir_send_job_status(JCR *jcr)
149 return jcr->sendJobStatus();
153 * Common routine for:
154 * dir_get_volume_info()
156 * dir_find_next_appendable_volume()
158 * NOTE!!! All calls to this routine must be protected by
159 * locking vol_info_mutex before calling it so that
160 * we don't have one thread modifying the parameters
161 * and another reading them.
163 * Returns: true on success and vol info in dcr->VolCatInfo
166 static bool do_get_volume_info(DCR *dcr)
169 BSOCK *dir = jcr->dir_bsock;
174 dcr->setVolCatInfo(false);
175 if (dir->recv() <= 0) {
176 Dmsg0(200, "getvolname error bnet_recv\n");
177 Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
180 memset(&vol, 0, sizeof(vol));
181 Dmsg1(100, "<dird %s", dir->msg);
182 n = sscanf(dir->msg, OK_media, vol.VolCatName,
183 &vol.VolCatJobs, &vol.VolCatFiles,
184 &vol.VolCatBlocks, &vol.VolCatBytes,
185 &vol.VolCatMounts, &vol.VolCatErrors,
186 &vol.VolCatWrites, &vol.VolCatMaxBytes,
187 &vol.VolCatCapacityBytes, vol.VolCatStatus,
188 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
189 &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
190 &vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
191 &vol.LabelType, &vol.VolMediaId);
193 Dmsg3(100, "Bad response from Dir fields=%d, len=%d: %s",
194 n, dir->msglen, dir->msg);
195 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
198 vol.InChanger = InChanger; /* bool in structure */
200 unbash_spaces(vol.VolCatName);
201 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
202 dcr->VolCatInfo = vol; /* structure assignment */
204 Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n",
205 vol.Slot, vol.VolCatName);
211 * Get Volume info for a specific volume from the Director's Database
213 * Returns: true on success (Director guarantees that Pool and MediaType
214 * are correct and VolStatus==Append or
215 * VolStatus==Recycle)
218 * Volume information returned in dcr->VolCatInfo
220 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
223 BSOCK *dir = jcr->dir_bsock;
226 dcr->setVolCatName(dcr->VolumeName);
227 bash_spaces(dcr->getVolCatName());
228 dir->fsend(Get_Vol_Info, jcr->Job, dcr->getVolCatName(),
229 writing==GET_VOL_INFO_FOR_WRITE?1:0);
230 Dmsg1(100, ">dird %s", dir->msg);
231 unbash_spaces(dcr->getVolCatName());
232 bool ok = do_get_volume_info(dcr);
240 * Get info on the next appendable volume in the Director's database
242 * Returns: true on success dcr->VolumeName is volume
243 * reserve_volume() called on Volume name
244 * false on failure dcr->VolumeName[0] == 0
245 * also sets dcr->found_in_use if at least one
246 * in use volume was found.
248 * Volume information returned in dcr
251 bool dir_find_next_appendable_volume(DCR *dcr)
254 BSOCK *dir = jcr->dir_bsock;
256 char lastVolume[MAX_NAME_LENGTH];
258 Dmsg2(200, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n",
259 dcr->is_reserved(), dcr->VolumeName);
262 * Try the twenty oldest or most available volumes. Note,
263 * the most available could already be mounted on another
264 * drive, so we continue looking for a not in use Volume.
268 dcr->clear_found_in_use();
270 for (int vol_index=1; vol_index < 20; vol_index++) {
271 bash_spaces(dcr->media_type);
272 bash_spaces(dcr->pool_name);
273 dir->fsend(Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
274 unbash_spaces(dcr->media_type);
275 unbash_spaces(dcr->pool_name);
276 Dmsg1(100, ">dird %s", dir->msg);
277 if (do_get_volume_info(dcr)) {
278 /* Give up if we get the same volume name twice */
279 if (lastVolume[0] && strcmp(lastVolume, dcr->VolumeName) == 0) {
280 Dmsg1(100, "Got same vol = %s\n", lastVolume);
283 bstrncpy(lastVolume, dcr->VolumeName, sizeof(lastVolume));
284 if (dcr->can_i_write_volume()) {
285 Dmsg1(100, "Call reserve_volume for write. Vol=%s\n", dcr->VolumeName);
286 if (reserve_volume(dcr, dcr->VolumeName) == NULL) {
287 Dmsg2(100, "Could not reserve volume %s on %s\n", dcr->VolumeName,
288 dcr->dev->print_name());
291 Dmsg1(100, "dir_find_next_appendable_volume return true. vol=%s\n",
296 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
297 /* If volume is not usable, it is in use by someone else */
298 dcr->set_found_in_use();
302 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
303 dcr->dev->print_name());
307 dcr->VolumeName[0] = 0;
317 * After writing a Volume, send the updated statistics
318 * back to the director. The information comes from the
321 bool dir_update_volume_info(DCR *dcr, bool label, bool update_LastWritten)
324 BSOCK *dir = jcr->dir_bsock;
325 DEVICE *dev = dcr->dev;
326 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
327 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50];
332 /* If system job, do not update catalog */
333 if (jcr->getJobType() == JT_SYSTEM) {
337 if (vol->VolCatName[0] == 0) {
338 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
339 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
343 /* Lock during Volume update */
345 Dmsg1(100, "Update cat VolBytes=%lld\n", vol->VolCatBytes);
346 /* Just labeled or relabeled the tape */
348 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
350 // if (update_LastWritten) {
351 vol->VolLastWritten = time(NULL);
353 pm_strcpy(VolumeName, vol->VolCatName);
354 bash_spaces(VolumeName);
355 InChanger = vol->InChanger;
356 dir->fsend(Update_media, jcr->Job,
357 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
358 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
359 vol->VolCatMounts, vol->VolCatErrors,
360 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
361 edit_uint64(vol->VolLastWritten, ed6),
362 vol->VolCatStatus, vol->Slot, label,
363 InChanger, /* bool in structure */
364 edit_int64(vol->VolReadTime, ed3),
365 edit_int64(vol->VolWriteTime, ed4),
366 edit_uint64(vol->VolFirstWritten, ed5),
368 Dmsg1(100, ">dird %s", dir->msg);
370 /* Do not lock device here because it may be locked from label */
371 if (!jcr->is_canceled()) {
372 if (!do_get_volume_info(dcr)) {
373 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
374 Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"),
375 vol->VolCatName, jcr->errmsg);
378 Dmsg1(420, "get_volume_info() %s", dir->msg);
379 /* Update dev Volume info in case something changed (e.g. expired) */
380 dev->VolCatInfo = dcr->VolCatInfo;
390 * After writing a Volume, create the JobMedia record.
392 bool dir_create_jobmedia_record(DCR *dcr, bool zero)
395 BSOCK *dir = jcr->dir_bsock;
398 /* If system job, do not update catalog */
399 if (jcr->getJobType() == JT_SYSTEM) {
403 /* Throw out records where FI is zero -- i.e. nothing done */
404 if (!zero && dcr->VolFirstIndex == 0 &&
405 (dcr->StartBlock != 0 || dcr->EndBlock != 0)) {
406 Dmsg0(100, "JobMedia FI=0 StartBlock!=0 record suppressed\n");
410 if (!dcr->WroteVol) {
411 return true; /* nothing written to tape */
414 dcr->WroteVol = false;
416 /* Send dummy place holder to avoid purging */
417 dir->fsend(Create_job_media, jcr->Job,
418 0 , 0, 0, 0, 0, 0, 0, 0, edit_uint64(dcr->VolMediaId, ed1));
420 dir->fsend(Create_job_media, jcr->Job,
421 dcr->VolFirstIndex, dcr->VolLastIndex,
422 dcr->StartFile, dcr->EndFile,
423 dcr->StartBlock, dcr->EndBlock,
424 dcr->Copy, dcr->Stripe,
425 edit_uint64(dcr->VolMediaId, ed1));
427 Dmsg1(100, ">dird %s", dir->msg);
428 if (dir->recv() <= 0) {
429 Dmsg0(190, "create_jobmedia error bnet_recv\n");
430 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
434 Dmsg1(100, "<dird %s", dir->msg);
435 if (strcmp(dir->msg, OK_create) != 0) {
436 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
437 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
445 * Update File Attribute data
446 * We do the following:
447 * 1. expand the bsock buffer to be large enough
448 * 2. Write a "header" into the buffer with serialized data
453 * data length that follows
454 * start of raw byte data from the Device record.
455 * Note, this is primarily for Attribute data, but can
456 * also handle any device record. The Director must know
457 * the raw byte data format that is defined for each Stream.
458 * Now Restore Objects pass through here STREAM_RESTORE_OBJECT
460 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
463 BSOCK *dir = jcr->dir_bsock;
466 #ifdef NO_ATTRIBUTES_TEST
470 dir->msg = check_pool_memory_size(dir->msg, sizeof(FileAttributes) +
471 MAX_NAME_LENGTH + sizeof(DEV_RECORD) + rec->data_len + 1);
472 dir->msglen = bsnprintf(dir->msg, sizeof(FileAttributes) +
473 MAX_NAME_LENGTH + 1, FileAttributes, jcr->Job);
474 ser_begin(dir->msg + dir->msglen, 0);
475 ser_uint32(rec->VolSessionId);
476 ser_uint32(rec->VolSessionTime);
477 ser_int32(rec->FileIndex);
478 ser_int32(rec->Stream);
479 ser_uint32(rec->data_len);
480 ser_bytes(rec->data, rec->data_len);
481 dir->msglen = ser_length(dir->msg);
482 Dmsg1(1800, ">dird %s\n", dir->msg); /* Attributes */
488 * Request the sysop to create an appendable volume
490 * Entered with device blocked.
491 * Leaves with device blocked.
493 * Returns: true on success (operator issues a mount command)
495 * Note, must create dev->errmsg on error return.
497 * On success, dcr->VolumeName and dcr->VolCatInfo contain
498 * information on suggested volume, but this may not be the
499 * same as what is actually mounted.
501 * When we return with success, the correct tape may or may not
502 * actually be mounted. The calling routine must read it and
505 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
507 int stat = W_TIMEOUT;
508 DEVICE *dev = dcr->dev;
510 bool got_vol = false;
512 if (job_canceled(jcr)) {
515 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
516 ASSERT(dev->blocked());
518 if (job_canceled(jcr)) {
520 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
521 jcr->Job, dev->print_name());
522 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
525 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
529 if (stat == W_TIMEOUT || stat == W_MOUNT) {
531 "Job %s is waiting. Cannot find any appendable volumes.\n"
532 "Please use the \"label\" command to create a new Volume for:\n"
535 " Media type: %s\n"),
540 Jmsg(jcr, M_MOUNT, 0, "%s", dev->errmsg);
541 Dmsg1(100, "%s", dev->errmsg);
545 jcr->sendJobStatus(JS_WaitMedia);
547 stat = wait_for_sysop(dcr);
548 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
550 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
554 if (stat == W_TIMEOUT) {
555 if (!double_dev_wait_time(dev)) {
556 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
557 dev->print_name(), jcr->Job);
558 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
559 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
560 return false; /* exceeded maximum waits */
564 if (stat == W_ERROR) {
566 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
567 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
570 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
574 jcr->sendJobStatus(JS_Running);
575 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
580 * Request to mount specific Volume
582 * Entered with device blocked and dcr->VolumeName is desired
584 * Leaves with device blocked.
586 * Returns: true on success (operator issues a mount command)
588 * Note, must create dev->errmsg on error return.
591 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int mode)
593 int stat = W_TIMEOUT;
594 DEVICE *dev = dcr->dev;
597 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
598 if (!dcr->VolumeName[0]) {
599 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
602 ASSERT(dev->blocked());
604 if (job_canceled(jcr)) {
605 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
606 jcr->Job, dev->print_name());
615 * If we are not polling, and the wait timeout or the
616 * user explicitly did a mount, send him the message.
619 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
621 if (mode == ST_APPEND) {
622 msg = _("Please mount Volume \"%s\" or label a new one for:\n"
626 " Media type: %s\n");
628 msg = _("Please mount Volume \"%s\" for:\n"
632 " Media type: %s\n");
634 Jmsg(jcr, M_MOUNT, 0, msg,
640 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
641 dcr->VolumeName, dev->print_name(), jcr->Job);
644 jcr->sendJobStatus(JS_WaitMount);
646 stat = wait_for_sysop(dcr); /* wait on device */
647 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
649 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
650 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
654 if (stat == W_TIMEOUT) {
655 if (!double_dev_wait_time(dev)) {
656 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
657 dev->print_name(), jcr->Job);
658 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
659 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
660 return false; /* exceeded maximum waits */
664 if (stat == W_ERROR) {
666 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
667 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
670 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
675 jcr->sendJobStatus(JS_Running);
676 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");