2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 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 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.
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
37 #include "bacula.h" /* pull in global headers */
38 #include "stored.h" /* pull in Storage Deamon headers */
40 /* Requests sent to the Director */
41 static char Find_media[] = "CatReq Job=%s FindMedia=%d pool_name=%s media_type=%s\n";
42 static char Get_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s write=%d\n";
43 static char Update_media[] = "CatReq Job=%s UpdateMedia VolName=%s"
44 " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%s VolMounts=%u"
45 " VolErrors=%u VolWrites=%u MaxVolBytes=%s EndTime=%d VolStatus=%s"
46 " Slot=%d relabel=%d InChanger=%d VolReadTime=%s VolWriteTime=%s"
47 " VolFirstWritten=%s VolParts=%u\n";
48 static char Create_job_media[] = "CatReq Job=%s CreateJobMedia"
49 " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u"
50 " StartBlock=%u EndBlock=%u Copy=%d Strip=%d MediaId=%s\n";
51 static char FileAttributes[] = "UpdCat Job=%s FileAttributes ";
52 static char Job_status[] = "Status Job=%s JobStatus=%d\n";
54 /* Responses received from the Director */
55 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
56 " VolBlocks=%lu VolBytes=%lld VolMounts=%lu VolErrors=%lu VolWrites=%lu"
57 " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
58 " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
59 " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
60 " VolParts=%lu LabelType=%ld MediaId=%lld\n";
63 static char OK_create[] = "1000 OK CreateJobMedia\n";
65 static pthread_mutex_t vol_info_mutex = PTHREAD_MUTEX_INITIALIZER;
69 static char Device_update[] = "DevUpd Job=%s device=%s "
70 "append=%d read=%d num_writers=%d "
71 "open=%d labeled=%d offline=%d "
72 "reserved=%d max_writers=%d "
73 "autoselect=%d autochanger=%d "
74 "changer_name=%s media_type=%s volume_name=%s\n";
77 /* Send update information about a device to Director */
78 bool dir_update_device(JCR *jcr, DEVICE *dev)
80 BSOCK *dir = jcr->dir_bsock;
81 POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
82 DEVRES *device = dev->device;
85 pm_strcpy(dev_name, device->hdr.name);
86 bash_spaces(dev_name);
87 if (dev->is_labeled()) {
88 pm_strcpy(VolumeName, dev->VolHdr.VolumeName);
90 pm_strcpy(VolumeName, "*");
92 bash_spaces(VolumeName);
93 pm_strcpy(MediaType, device->media_type);
94 bash_spaces(MediaType);
95 if (device->changer_res) {
96 pm_strcpy(ChangerName, device->changer_res->hdr.name);
97 bash_spaces(ChangerName);
99 pm_strcpy(ChangerName, "*");
101 ok =bnet_fsend(dir, Device_update,
104 dev->can_append()!=0,
105 dev->can_read()!=0, dev->num_writers,
106 dev->is_open()!=0, dev->is_labeled()!=0,
107 dev->is_offline()!=0, dev->reserved_device,
108 dev->is_tape()?100000:1,
110 ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
111 Dmsg1(100, ">dird: %s\n", dir->msg);
115 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
117 BSOCK *dir = jcr->dir_bsock;
118 POOL_MEM dev_name, MediaType;
122 pm_strcpy(dev_name, changer->hdr.name);
123 bash_spaces(dev_name);
124 device = (DEVRES *)changer->device->first();
125 pm_strcpy(MediaType, device->media_type);
126 bash_spaces(MediaType);
127 /* This is mostly to indicate that we are here */
128 ok = bnet_fsend(dir, Device_update,
130 dev_name.c_str(), /* Changer name */
131 0, 0, 0, /* append, read, num_writers */
132 0, 0, 0, /* is_open, is_labeled, offline */
133 0, 0, /* reserved, max_writers */
135 changer->device->size(), /* Number of devices */
137 "*", /* ChangerName */
138 MediaType.c_str(), /* MediaType */
140 Dmsg1(100, ">dird: %s\n", dir->msg);
147 * Send current JobStatus to Director
149 bool dir_send_job_status(JCR *jcr)
151 return bnet_fsend(jcr->dir_bsock, Job_status, jcr->Job, jcr->JobStatus);
155 * Common routine for:
156 * dir_get_volume_info()
158 * dir_find_next_appendable_volume()
160 * NOTE!!! All calls to this routine must be protected by
161 * locking vol_info_mutex before calling it so that
162 * we don't have one thread modifying the parameters
163 * and another reading them.
165 * Returns: true on success and vol info in dcr->VolCatInfo
168 static bool do_get_volume_info(DCR *dcr)
171 BSOCK *dir = jcr->dir_bsock;
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\n", 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", n, dir->msglen, dir->msg);
195 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
198 vol.InChanger = InChanger; /* bool in structure */
199 unbash_spaces(vol.VolCatName);
200 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
201 dcr->VolCatInfo = vol; /* structure assignment */
203 Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n",
204 vol.Slot, vol.VolCatName);
210 * Get Volume info for a specific volume from the Director's Database
212 * Returns: true on success (Director guarantees that Pool and MediaType
213 * are correct and VolStatus==Append or
214 * VolStatus==Recycle)
217 * Volume information returned in dcr->VolCatInfo
219 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
222 BSOCK *dir = jcr->dir_bsock;
225 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
226 bash_spaces(dcr->VolCatInfo.VolCatName);
227 dir->fsend(Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
228 writing==GET_VOL_INFO_FOR_WRITE?1:0);
229 Dmsg1(100, ">dird: %s\n", dir->msg);
230 unbash_spaces(dcr->VolCatInfo.VolCatName);
231 bool ok = do_get_volume_info(dcr);
239 * Get info on the next appendable volume in the Director's database
241 * Returns: true on success dcr->VolumeName is volume
242 * reserve_volume() called on Volume name
243 * false on failure dcr->VolumeName[0] == 0
244 * also sets dcr->volume_in_use if at least one
245 * in use volume was found.
247 * Volume information returned in dcr
250 bool dir_find_next_appendable_volume(DCR *dcr)
253 BSOCK *dir = jcr->dir_bsock;
256 Dmsg0(200, "dir_find_next_appendable_volume\n");
258 * Try the twenty oldest or most available volumes. Note,
259 * the most available could already be mounted on another
260 * drive, so we continue looking for a not in use Volume.
264 dcr->volume_in_use = false;
265 for (int vol_index=1; vol_index < 20; vol_index++) {
266 bash_spaces(dcr->media_type);
267 bash_spaces(dcr->pool_name);
268 dir->fsend(Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
269 unbash_spaces(dcr->media_type);
270 unbash_spaces(dcr->pool_name);
271 Dmsg1(100, ">dird: %s", dir->msg);
272 bool ok = do_get_volume_info(dcr);
274 if (!is_volume_in_use(dcr)) {
278 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
279 dcr->volume_in_use = true;
283 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
284 dcr->dev->print_name());
290 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
291 if (reserve_volume(dcr, dcr->VolumeName) == 0) {
292 Dmsg2(100, "Could not reserve volume %s on %s\n", dcr->VolumeName,
293 dcr->dev->print_name());
297 unlock_reservations();
302 dcr->VolumeName[0] = 0;
304 unlock_reservations();
310 * After writing a Volume, send the updated statistics
311 * back to the director. The information comes from the
314 bool dir_update_volume_info(DCR *dcr, bool label)
317 BSOCK *dir = jcr->dir_bsock;
318 DEVICE *dev = dcr->dev;
319 time_t LastWritten = time(NULL);
320 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
321 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
326 /* If system job, do not update catalog */
327 if (jcr->JobType == JT_SYSTEM) {
331 if (vol->VolCatName[0] == 0) {
332 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
333 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
337 /* Lock during Volume update */
339 Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
340 /* Just labeled or relabeled the tape */
342 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
344 pm_strcpy(VolumeName, vol->VolCatName);
345 bash_spaces(VolumeName);
346 InChanger = vol->InChanger;
347 bnet_fsend(dir, Update_media, jcr->Job,
348 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
349 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
350 vol->VolCatMounts, vol->VolCatErrors,
351 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
352 LastWritten, vol->VolCatStatus, vol->Slot, label,
353 InChanger, /* bool in structure */
354 edit_int64(vol->VolReadTime, ed3),
355 edit_int64(vol->VolWriteTime, ed4),
356 edit_uint64(vol->VolFirstWritten, ed5),
358 Dmsg1(100, ">dird: %s", dir->msg);
360 /* Do not lock device here because it may be locked from label */
361 if (!do_get_volume_info(dcr)) {
362 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
363 Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"),
364 vol->VolCatName, jcr->errmsg);
367 Dmsg1(420, "get_volume_info(): %s", dir->msg);
368 /* Update dev Volume info in case something changed (e.g. expired) */
369 dev->VolCatInfo = dcr->VolCatInfo;
378 * After writing a Volume, create the JobMedia record.
380 bool dir_create_jobmedia_record(DCR *dcr)
383 BSOCK *dir = jcr->dir_bsock;
386 /* If system job, do not update catalog */
387 if (jcr->JobType == JT_SYSTEM) {
391 if (!dcr->WroteVol) {
392 return true; /* nothing written to tape */
395 dcr->WroteVol = false;
396 bnet_fsend(dir, Create_job_media, jcr->Job,
397 dcr->VolFirstIndex, dcr->VolLastIndex,
398 dcr->StartFile, dcr->EndFile,
399 dcr->StartBlock, dcr->EndBlock,
400 dcr->Copy, dcr->Stripe,
401 edit_uint64(dcr->dev->VolCatInfo.VolMediaId, ed1));
402 Dmsg1(100, ">dird: %s", dir->msg);
403 if (bnet_recv(dir) <= 0) {
404 Dmsg0(190, "create_jobmedia error bnet_recv\n");
405 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
409 Dmsg1(100, "<dir: %s", dir->msg);
410 if (strcmp(dir->msg, OK_create) != 0) {
411 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
412 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
420 * Update File Attribute data
422 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
425 BSOCK *dir = jcr->dir_bsock;
428 #ifdef NO_ATTRIBUTES_TEST
432 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
433 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
434 sizeof(DEV_RECORD) + rec->data_len);
435 ser_begin(dir->msg + dir->msglen, 0);
436 ser_uint32(rec->VolSessionId);
437 ser_uint32(rec->VolSessionTime);
438 ser_int32(rec->FileIndex);
439 ser_int32(rec->Stream);
440 ser_uint32(rec->data_len);
441 ser_bytes(rec->data, rec->data_len);
442 dir->msglen = ser_length(dir->msg);
443 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
444 return bnet_send(dir);
449 * Request the sysop to create an appendable volume
451 * Entered with device blocked.
452 * Leaves with device blocked.
454 * Returns: true on success (operator issues a mount command)
456 * Note, must create dev->errmsg on error return.
458 * On success, dcr->VolumeName and dcr->VolCatInfo contain
459 * information on suggested volume, but this may not be the
460 * same as what is actually mounted.
462 * When we return with success, the correct tape may or may not
463 * actually be mounted. The calling routine must read it and
466 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
468 int stat = W_TIMEOUT;
469 DEVICE *dev = dcr->dev;
471 bool got_vol = false;
473 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
474 ASSERT(dev->blocked());
476 if (job_canceled(jcr)) {
478 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
479 jcr->Job, dev->print_name());
480 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
484 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
489 if (stat == W_TIMEOUT || stat == W_MOUNT) {
490 Jmsg(jcr, M_MOUNT, 0, _(
491 "Job %s waiting. Cannot find any appendable volumes.\n"
492 "Please use the \"label\" command to create a new Volume for:\n"
495 " Media type: %s\n"),
503 set_jcr_job_status(jcr, JS_WaitMedia);
504 dir_send_job_status(jcr);
506 stat = wait_for_sysop(dcr);
507 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
509 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
513 if (stat == W_TIMEOUT) {
514 if (!double_dev_wait_time(dev)) {
515 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
516 dev->print_name(), jcr->Job);
517 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
518 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
519 return false; /* exceeded maximum waits */
523 if (stat == W_ERROR) {
525 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
526 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
529 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
531 set_jcr_job_status(jcr, JS_Running);
532 dir_send_job_status(jcr);
533 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
538 * Request to mount specific Volume
540 * Entered with device blocked and dcr->VolumeName is desired
542 * Leaves with device blocked.
544 * Returns: true on success (operator issues a mount command)
546 * Note, must create dev->errmsg on error return.
549 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
551 int stat = W_TIMEOUT;
552 DEVICE *dev = dcr->dev;
555 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
556 if (!dcr->VolumeName[0]) {
557 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
560 ASSERT(dev->blocked());
562 if (job_canceled(jcr)) {
563 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
564 jcr->Job, dev->print_name());
573 * If we are not polling, and the wait timeout or the
574 * user explicitly did a mount, send him the message.
577 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
578 Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" or label a new one for:\n"
582 " Media type: %s\n"),
588 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
589 dcr->VolumeName, dev->print_name(), jcr->Job);
592 set_jcr_job_status(jcr, JS_WaitMount);
593 dir_send_job_status(jcr);
595 stat = wait_for_sysop(dcr); /* wait on device */
596 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
598 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
599 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
603 if (stat == W_TIMEOUT) {
604 if (!double_dev_wait_time(dev)) {
605 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
606 dev->print_name(), jcr->Job);
607 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
608 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
609 return false; /* exceeded maximum waits */
613 if (stat == W_ERROR) {
615 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
616 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
619 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
624 set_jcr_job_status(jcr, JS_Running);
625 dir_send_job_status(jcr);
626 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");