2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2010 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.
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 ";
51 static char Job_status[] = "Status Job=%s JobStatus=%d\n";
53 /* Responses received from the Director */
54 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
55 " VolBlocks=%lu VolBytes=%lld VolMounts=%lu VolErrors=%lu VolWrites=%lu"
56 " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
57 " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
58 " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
59 " VolParts=%lu LabelType=%ld MediaId=%lld\n";
62 static char OK_create[] = "1000 OK CreateJobMedia\n";
64 static pthread_mutex_t vol_info_mutex = PTHREAD_MUTEX_INITIALIZER;
68 static char Device_update[] = "DevUpd Job=%s device=%s "
69 "append=%d read=%d num_writers=%d "
70 "open=%d labeled=%d offline=%d "
71 "reserved=%d max_writers=%d "
72 "autoselect=%d autochanger=%d "
73 "changer_name=%s media_type=%s volume_name=%s\n";
76 /* Send update information about a device to Director */
77 bool dir_update_device(JCR *jcr, DEVICE *dev)
79 BSOCK *dir = jcr->dir_bsock;
80 POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
81 DEVRES *device = dev->device;
84 pm_strcpy(dev_name, device->hdr.name);
85 bash_spaces(dev_name);
86 if (dev->is_labeled()) {
87 pm_strcpy(VolumeName, dev->VolHdr.VolumeName);
89 pm_strcpy(VolumeName, "*");
91 bash_spaces(VolumeName);
92 pm_strcpy(MediaType, device->media_type);
93 bash_spaces(MediaType);
94 if (device->changer_res) {
95 pm_strcpy(ChangerName, device->changer_res->hdr.name);
96 bash_spaces(ChangerName);
98 pm_strcpy(ChangerName, "*");
100 ok = dir->fsend(Device_update,
103 dev->can_append()!=0,
104 dev->can_read()!=0, dev->num_writers,
105 dev->is_open()!=0, dev->is_labeled()!=0,
106 dev->is_offline()!=0, dev->reserved_device,
107 dev->is_tape()?100000:1,
109 ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
110 Dmsg1(100, ">dird: %s\n", dir->msg);
114 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
116 BSOCK *dir = jcr->dir_bsock;
117 POOL_MEM dev_name, MediaType;
121 pm_strcpy(dev_name, changer->hdr.name);
122 bash_spaces(dev_name);
123 device = (DEVRES *)changer->device->first();
124 pm_strcpy(MediaType, device->media_type);
125 bash_spaces(MediaType);
126 /* This is mostly to indicate that we are here */
127 ok = dir->fsend(Device_update,
129 dev_name.c_str(), /* Changer name */
130 0, 0, 0, /* append, read, num_writers */
131 0, 0, 0, /* is_open, is_labeled, offline */
132 0, 0, /* reserved, max_writers */
134 changer->device->size(), /* Number of devices */
136 "*", /* ChangerName */
137 MediaType.c_str(), /* MediaType */
139 Dmsg1(100, ">dird: %s\n", dir->msg);
146 * Send current JobStatus to Director
148 bool dir_send_job_status(JCR *jcr)
150 return jcr->dir_bsock->fsend(Job_status, jcr->Job, jcr->JobStatus);
154 * Common routine for:
155 * dir_get_volume_info()
157 * dir_find_next_appendable_volume()
159 * NOTE!!! All calls to this routine must be protected by
160 * locking vol_info_mutex before calling it so that
161 * we don't have one thread modifying the parameters
162 * and another reading them.
164 * Returns: true on success and vol info in dcr->VolCatInfo
167 static bool do_get_volume_info(DCR *dcr)
170 BSOCK *dir = jcr->dir_bsock;
175 dcr->setVolCatInfo(false);
176 if (dir->recv() <= 0) {
177 Dmsg0(200, "getvolname error bnet_recv\n");
178 Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
181 memset(&vol, 0, sizeof(vol));
182 Dmsg1(100, "<dird %s", dir->msg);
183 n = sscanf(dir->msg, OK_media, vol.VolCatName,
184 &vol.VolCatJobs, &vol.VolCatFiles,
185 &vol.VolCatBlocks, &vol.VolCatBytes,
186 &vol.VolCatMounts, &vol.VolCatErrors,
187 &vol.VolCatWrites, &vol.VolCatMaxBytes,
188 &vol.VolCatCapacityBytes, vol.VolCatStatus,
189 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
190 &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
191 &vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
192 &vol.LabelType, &vol.VolMediaId);
194 Dmsg3(100, "Bad response from Dir fields=%d, len=%d: %s",
195 n, dir->msglen, dir->msg);
196 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
199 vol.InChanger = InChanger; /* bool in structure */
201 unbash_spaces(vol.VolCatName);
202 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
203 dcr->VolCatInfo = vol; /* structure assignment */
205 Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n",
206 vol.Slot, vol.VolCatName);
212 * Get Volume info for a specific volume from the Director's Database
214 * Returns: true on success (Director guarantees that Pool and MediaType
215 * are correct and VolStatus==Append or
216 * VolStatus==Recycle)
219 * Volume information returned in dcr->VolCatInfo
221 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
224 BSOCK *dir = jcr->dir_bsock;
227 dcr->setVolCatName(dcr->VolumeName);
228 bash_spaces(dcr->getVolCatName());
229 dir->fsend(Get_Vol_Info, jcr->Job, dcr->getVolCatName(),
230 writing==GET_VOL_INFO_FOR_WRITE?1:0);
231 Dmsg1(100, ">dird %s", dir->msg);
232 unbash_spaces(dcr->getVolCatName());
233 bool ok = do_get_volume_info(dcr);
241 * Get info on the next appendable volume in the Director's database
243 * Returns: true on success dcr->VolumeName is volume
244 * reserve_volume() called on Volume name
245 * false on failure dcr->VolumeName[0] == 0
246 * also sets dcr->found_in_use if at least one
247 * in use volume was found.
249 * Volume information returned in dcr
252 bool dir_find_next_appendable_volume(DCR *dcr)
255 BSOCK *dir = jcr->dir_bsock;
257 char lastVolume[MAX_NAME_LENGTH];
259 Dmsg2(200, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n",
260 dcr->is_reserved(), dcr->VolumeName);
263 * Try the twenty oldest or most available volumes. Note,
264 * the most available could already be mounted on another
265 * drive, so we continue looking for a not in use Volume.
269 dcr->clear_found_in_use();
271 for (int vol_index=1; vol_index < 20; vol_index++) {
272 bash_spaces(dcr->media_type);
273 bash_spaces(dcr->pool_name);
274 dir->fsend(Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
275 unbash_spaces(dcr->media_type);
276 unbash_spaces(dcr->pool_name);
277 Dmsg1(100, ">dird %s", dir->msg);
278 if (do_get_volume_info(dcr)) {
279 /* Give up if we get the same volume name twice */
280 if (lastVolume[0] && strcmp(lastVolume, dcr->VolumeName) == 0) {
281 Dmsg1(100, "Got same vol = %s\n", lastVolume);
284 bstrncpy(lastVolume, dcr->VolumeName, sizeof(lastVolume));
285 if (dcr->can_i_write_volume()) {
286 Dmsg1(100, "Call reserve_volume. Vol=%s\n", dcr->VolumeName);
287 if (reserve_volume(dcr, dcr->VolumeName) == NULL) {
288 Dmsg2(100, "Could not reserve volume %s on %s\n", dcr->VolumeName,
289 dcr->dev->print_name());
292 Dmsg1(100, "dir_find_next_appendable_volume return true. vol=%s\n",
297 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
298 /* If volume is not usable, it is in use by someone else */
299 dcr->set_found_in_use();
303 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
304 dcr->dev->print_name());
308 dcr->VolumeName[0] = 0;
318 * After writing a Volume, send the updated statistics
319 * back to the director. The information comes from the
322 bool dir_update_volume_info(DCR *dcr, bool label, bool update_LastWritten)
325 BSOCK *dir = jcr->dir_bsock;
326 DEVICE *dev = dcr->dev;
327 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
328 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50];
333 /* If system job, do not update catalog */
334 if (jcr->getJobType() == JT_SYSTEM) {
338 if (vol->VolCatName[0] == 0) {
339 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
340 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
344 /* Lock during Volume update */
346 Dmsg1(100, "Update cat VolBytes=%lld\n", vol->VolCatBytes);
347 /* Just labeled or relabeled the tape */
349 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
351 // if (update_LastWritten) {
352 vol->VolLastWritten = time(NULL);
354 pm_strcpy(VolumeName, vol->VolCatName);
355 bash_spaces(VolumeName);
356 InChanger = vol->InChanger;
357 dir->fsend(Update_media, jcr->Job,
358 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
359 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
360 vol->VolCatMounts, vol->VolCatErrors,
361 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
362 edit_uint64(vol->VolLastWritten, ed6),
363 vol->VolCatStatus, vol->Slot, label,
364 InChanger, /* bool in structure */
365 edit_int64(vol->VolReadTime, ed3),
366 edit_int64(vol->VolWriteTime, ed4),
367 edit_uint64(vol->VolFirstWritten, ed5),
369 Dmsg1(100, ">dird %s", dir->msg);
371 /* Do not lock device here because it may be locked from label */
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;
389 * After writing a Volume, create the JobMedia record.
391 bool dir_create_jobmedia_record(DCR *dcr, bool zero)
394 BSOCK *dir = jcr->dir_bsock;
397 /* If system job, do not update catalog */
398 if (jcr->getJobType() == JT_SYSTEM) {
402 /* Throw out records where FI is zero -- i.e. nothing done */
403 if (!zero && dcr->VolFirstIndex == 0 &&
404 (dcr->StartBlock != 0 || dcr->EndBlock != 0)) {
405 Dmsg0(100, "JobMedia FI=0 StartBlock!=0 record suppressed\n");
409 if (!dcr->WroteVol) {
410 return true; /* nothing written to tape */
413 dcr->WroteVol = false;
415 /* Send dummy place holder to avoid purging */
416 dir->fsend(Create_job_media, jcr->Job,
417 0 , 0, 0, 0, 0, 0, 0, 0, edit_uint64(dcr->VolMediaId, ed1));
419 dir->fsend(Create_job_media, jcr->Job,
420 dcr->VolFirstIndex, dcr->VolLastIndex,
421 dcr->StartFile, dcr->EndFile,
422 dcr->StartBlock, dcr->EndBlock,
423 dcr->Copy, dcr->Stripe,
424 edit_uint64(dcr->VolMediaId, ed1));
426 Dmsg1(100, ">dird %s", dir->msg);
427 if (dir->recv() <= 0) {
428 Dmsg0(190, "create_jobmedia error bnet_recv\n");
429 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
433 Dmsg1(100, "<dird %s", dir->msg);
434 if (strcmp(dir->msg, OK_create) != 0) {
435 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
436 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
444 * Update File Attribute data
446 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
449 BSOCK *dir = jcr->dir_bsock;
452 #ifdef NO_ATTRIBUTES_TEST
456 dir->msg = check_pool_memory_size(dir->msg, sizeof(FileAttributes) +
457 MAX_NAME_LENGTH + sizeof(DEV_RECORD) + rec->data_len + 1);
458 dir->msglen = bsnprintf(dir->msg, sizeof(FileAttributes) +
459 MAX_NAME_LENGTH + 1, FileAttributes, jcr->Job);
460 ser_begin(dir->msg + dir->msglen, 0);
461 ser_uint32(rec->VolSessionId);
462 ser_uint32(rec->VolSessionTime);
463 ser_int32(rec->FileIndex);
464 ser_int32(rec->Stream);
465 ser_uint32(rec->data_len);
466 ser_bytes(rec->data, rec->data_len);
467 dir->msglen = ser_length(dir->msg);
468 Dmsg1(1800, ">dird %s\n", dir->msg); /* Attributes */
469 if (rec->Stream == STREAM_UNIX_ATTRIBUTES ||
470 rec->Stream == STREAM_UNIX_ATTRIBUTES_EX) {
471 dir->set_data_end(); /* set offset of last valid data */
478 * Request the sysop to create an appendable volume
480 * Entered with device blocked.
481 * Leaves with device blocked.
483 * Returns: true on success (operator issues a mount command)
485 * Note, must create dev->errmsg on error return.
487 * On success, dcr->VolumeName and dcr->VolCatInfo contain
488 * information on suggested volume, but this may not be the
489 * same as what is actually mounted.
491 * When we return with success, the correct tape may or may not
492 * actually be mounted. The calling routine must read it and
495 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
497 int stat = W_TIMEOUT;
498 DEVICE *dev = dcr->dev;
500 bool got_vol = false;
502 if (job_canceled(jcr)) {
505 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
506 ASSERT(dev->blocked());
508 if (job_canceled(jcr)) {
510 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
511 jcr->Job, dev->print_name());
512 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
515 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
519 if (stat == W_TIMEOUT || stat == W_MOUNT) {
521 "Job %s is waiting. Cannot find any appendable volumes.\n"
522 "Please use the \"label\" command to create a new Volume for:\n"
525 " Media type: %s\n"),
530 Jmsg(jcr, M_MOUNT, 0, "%s", dev->errmsg);
531 Dmsg1(100, "%s", dev->errmsg);
535 set_jcr_job_status(jcr, JS_WaitMedia);
536 dir_send_job_status(jcr);
538 stat = wait_for_sysop(dcr);
539 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
541 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
545 if (stat == W_TIMEOUT) {
546 if (!double_dev_wait_time(dev)) {
547 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
548 dev->print_name(), jcr->Job);
549 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
550 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
551 return false; /* exceeded maximum waits */
555 if (stat == W_ERROR) {
557 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
558 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
561 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
565 set_jcr_job_status(jcr, JS_Running);
566 dir_send_job_status(jcr);
567 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
572 * Request to mount specific Volume
574 * Entered with device blocked and dcr->VolumeName is desired
576 * Leaves with device blocked.
578 * Returns: true on success (operator issues a mount command)
580 * Note, must create dev->errmsg on error return.
583 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int mode)
585 int stat = W_TIMEOUT;
586 DEVICE *dev = dcr->dev;
589 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
590 if (!dcr->VolumeName[0]) {
591 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
594 ASSERT(dev->blocked());
596 if (job_canceled(jcr)) {
597 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
598 jcr->Job, dev->print_name());
607 * If we are not polling, and the wait timeout or the
608 * user explicitly did a mount, send him the message.
611 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
613 if (mode == ST_APPEND) {
614 msg = _("Please mount Volume \"%s\" or label a new one for:\n"
618 " Media type: %s\n");
620 msg = _("Please mount Volume \"%s\" for:\n"
624 " Media type: %s\n");
626 Jmsg(jcr, M_MOUNT, 0, msg,
632 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
633 dcr->VolumeName, dev->print_name(), jcr->Job);
636 set_jcr_job_status(jcr, JS_WaitMount);
637 dir_send_job_status(jcr);
639 stat = wait_for_sysop(dcr); /* wait on device */
640 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
642 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
643 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
647 if (stat == W_TIMEOUT) {
648 if (!double_dev_wait_time(dev)) {
649 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
650 dev->print_name(), jcr->Job);
651 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
652 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
653 return false; /* exceeded maximum waits */
657 if (stat == W_ERROR) {
659 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
660 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
663 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
668 set_jcr_job_status(jcr, JS_Running);
669 dir_send_job_status(jcr);
670 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");