2 * Subroutines to handle Catalog reqests sent to the Director
3 * Reqests/commands from the Director are handled in dircmd.c
5 * Kern Sibbald, December 2000
10 Bacula® - The Network Backup Solution
12 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
16 This program is Free Software; you can redistribute it and/or
17 modify it under the terms of version two of the GNU General Public
18 License as published by the Free Software Foundation plus additions
19 that are listed in the file LICENSE.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 Bacula® is a registered trademark of John Walker.
32 The licensor of Bacula is the Free Software Foundation Europe
33 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34 Switzerland, email:ftf@fsfeurope.org.
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 (bnet_recv(dir) <= 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 Dmsg2(100, "Bad response from Dir fields=%d: %s", n, 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 bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
228 writing==GET_VOL_INFO_FOR_WRITE?1:0);
229 Dmsg1(100, ">dird: %s", 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
240 * Returns: true on success
243 * Volume information returned in dcr
246 bool dir_find_next_appendable_volume(DCR *dcr)
249 BSOCK *dir = jcr->dir_bsock;
252 Dmsg0(200, "dir_find_next_appendable_volume\n");
254 * Try the twenty oldest or most available volumes. Note,
255 * the most available could already be mounted on another
256 * drive, so we continue looking for a not in use Volume.
260 for (int vol_index=1; vol_index < 20; vol_index++) {
261 bash_spaces(dcr->media_type);
262 bash_spaces(dcr->pool_name);
263 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
264 unbash_spaces(dcr->media_type);
265 unbash_spaces(dcr->pool_name);
266 Dmsg1(100, ">dird: %s", dir->msg);
267 bool ok = do_get_volume_info(dcr);
269 if (dcr->any_volume || !is_volume_in_use(dcr)) {
273 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
277 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
278 dcr->dev->print_name());
284 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
285 new_volume(dcr, dcr->VolumeName); /* reserve volume */
287 unlock_reservations();
290 dcr->VolumeName[0] = 0;
292 unlock_reservations();
298 * After writing a Volume, send the updated statistics
299 * back to the director. The information comes from the
302 bool dir_update_volume_info(DCR *dcr, bool label)
305 BSOCK *dir = jcr->dir_bsock;
306 DEVICE *dev = dcr->dev;
307 time_t LastWritten = time(NULL);
308 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
309 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
314 /* If system job, do not update catalog */
315 if (jcr->JobType == JT_SYSTEM) {
319 if (vol->VolCatName[0] == 0) {
320 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
321 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
324 if (dev->can_read()) {
325 Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
326 Pmsg0(000, _("Attempt to update_volume_info in read mode!!!\n"));
330 /* Lock during Volume update */
332 Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
333 /* Just labeled or relabeled the tape */
335 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
337 pm_strcpy(VolumeName, vol->VolCatName);
338 bash_spaces(VolumeName);
339 InChanger = vol->InChanger;
340 bnet_fsend(dir, Update_media, jcr->Job,
341 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
342 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
343 vol->VolCatMounts, vol->VolCatErrors,
344 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
345 LastWritten, vol->VolCatStatus, vol->Slot, label,
346 InChanger, /* bool in structure */
347 edit_uint64(vol->VolReadTime, ed3),
348 edit_uint64(vol->VolWriteTime, ed4),
349 edit_uint64(vol->VolFirstWritten, ed5),
351 Dmsg1(100, ">dird: %s", dir->msg);
353 /* Do not lock device here because it may be locked from label */
354 if (!do_get_volume_info(dcr)) {
355 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
356 Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"),
357 vol->VolCatName, jcr->errmsg);
360 Dmsg1(420, "get_volume_info(): %s", dir->msg);
361 /* Update dev Volume info in case something changed (e.g. expired) */
362 dev->VolCatInfo = dcr->VolCatInfo;
371 * After writing a Volume, create the JobMedia record.
373 bool dir_create_jobmedia_record(DCR *dcr)
376 BSOCK *dir = jcr->dir_bsock;
379 /* If system job, do not update catalog */
380 if (jcr->JobType == JT_SYSTEM) {
384 if (!dcr->WroteVol) {
385 return true; /* nothing written to tape */
388 dcr->WroteVol = false;
389 bnet_fsend(dir, Create_job_media, jcr->Job,
390 dcr->VolFirstIndex, dcr->VolLastIndex,
391 dcr->StartFile, dcr->EndFile,
392 dcr->StartBlock, dcr->EndBlock,
393 dcr->Copy, dcr->Stripe,
394 edit_uint64(dcr->dev->VolCatInfo.VolMediaId, ed1));
395 Dmsg1(100, ">dird: %s", dir->msg);
396 if (bnet_recv(dir) <= 0) {
397 Dmsg0(190, "create_jobmedia error bnet_recv\n");
398 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
402 Dmsg1(100, "<dir: %s", dir->msg);
403 if (strcmp(dir->msg, OK_create) != 0) {
404 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
405 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
413 * Update File Attribute data
415 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
418 BSOCK *dir = jcr->dir_bsock;
421 #ifdef NO_ATTRIBUTES_TEST
425 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
426 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
427 sizeof(DEV_RECORD) + rec->data_len);
428 ser_begin(dir->msg + dir->msglen, 0);
429 ser_uint32(rec->VolSessionId);
430 ser_uint32(rec->VolSessionTime);
431 ser_int32(rec->FileIndex);
432 ser_int32(rec->Stream);
433 ser_uint32(rec->data_len);
434 ser_bytes(rec->data, rec->data_len);
435 dir->msglen = ser_length(dir->msg);
436 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
437 return bnet_send(dir);
442 * Request the sysop to create an appendable volume
444 * Entered with device blocked.
445 * Leaves with device blocked.
447 * Returns: true on success (operator issues a mount command)
449 * Note, must create dev->errmsg on error return.
451 * On success, dcr->VolumeName and dcr->VolCatInfo contain
452 * information on suggested volume, but this may not be the
453 * same as what is actually mounted.
455 * When we return with success, the correct tape may or may not
456 * actually be mounted. The calling routine must read it and
459 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
461 int stat = W_TIMEOUT;
462 DEVICE *dev = dcr->dev;
464 bool got_vol = false;
466 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
467 ASSERT(dev->dev_blocked);
469 if (job_canceled(jcr)) {
471 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
472 jcr->Job, dev->print_name());
473 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
477 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
482 if (stat == W_TIMEOUT || stat == W_MOUNT) {
483 Jmsg(jcr, M_MOUNT, 0, _(
484 "Job %s waiting. Cannot find any appendable volumes.\n"
485 "Please use the \"label\" command to create a new Volume for:\n"
496 set_jcr_job_status(jcr, JS_WaitMedia);
497 dir_send_job_status(jcr);
499 stat = wait_for_sysop(dcr);
500 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
502 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
506 if (stat == W_TIMEOUT) {
507 if (!double_dev_wait_time(dev)) {
508 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
509 dev->print_name(), jcr->Job);
510 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
511 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
512 return false; /* exceeded maximum waits */
516 if (stat == W_ERROR) {
518 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
519 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
522 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
524 set_jcr_job_status(jcr, JS_Running);
525 dir_send_job_status(jcr);
526 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
531 * Request to mount specific Volume
533 * Entered with device blocked and dcr->VolumeName is desired
535 * Leaves with device blocked.
537 * Returns: true on success (operator issues a mount command)
539 * Note, must create dev->errmsg on error return.
542 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
544 int stat = W_TIMEOUT;
545 DEVICE *dev = dcr->dev;
548 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
549 if (!dcr->VolumeName[0]) {
550 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
553 ASSERT(dev->dev_blocked);
555 if (job_canceled(jcr)) {
556 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
557 jcr->Job, dev->print_name());
566 * If we are not polling, and the wait timeout or the
567 * user explicitly did a mount, send him the message.
570 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
571 Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
572 dcr->VolumeName, dev->print_name(), jcr->Job);
573 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
574 dcr->VolumeName, dev->print_name(), jcr->Job);
577 set_jcr_job_status(jcr, JS_WaitMount);
578 dir_send_job_status(jcr);
580 stat = wait_for_sysop(dcr); /* wait on device */
581 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
583 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
584 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
588 if (stat == W_TIMEOUT) {
589 if (!double_dev_wait_time(dev)) {
590 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
591 dev->print_name(), jcr->Job);
592 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
593 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
594 return false; /* exceeded maximum waits */
598 if (stat == W_ERROR) {
600 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
601 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
604 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
607 set_jcr_job_status(jcr, JS_Running);
608 dir_send_job_status(jcr);
609 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");