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-2006 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";
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 =bnet_fsend(dir, 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 = bnet_fsend(dir, 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 bnet_fsend(jcr->dir_bsock, Job_status, jcr->Job, jcr->JobStatus);
153 * Common routine for:
154 * dir_get_volume_info()
156 * dir_find_next_appendable_volume()
158 * Returns: true on success and vol info in dcr->VolCatInfo
161 static bool do_get_volume_info(DCR *dcr)
164 BSOCK *dir = jcr->dir_bsock;
169 dcr->VolumeName[0] = 0; /* No volume */
170 if (bnet_recv(dir) <= 0) {
171 Dmsg0(200, "getvolname error bnet_recv\n");
172 Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
175 memset(&vol, 0, sizeof(vol));
176 Dmsg1(100, "<dird %s", dir->msg);
177 n = sscanf(dir->msg, OK_media, vol.VolCatName,
178 &vol.VolCatJobs, &vol.VolCatFiles,
179 &vol.VolCatBlocks, &vol.VolCatBytes,
180 &vol.VolCatMounts, &vol.VolCatErrors,
181 &vol.VolCatWrites, &vol.VolCatMaxBytes,
182 &vol.VolCatCapacityBytes, vol.VolCatStatus,
183 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
184 &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
185 &vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
186 &vol.LabelType, &vol.VolMediaId);
188 Dmsg2(100, "Bad response from Dir fields=%d: %s", n, dir->msg);
189 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
192 vol.InChanger = InChanger; /* bool in structure */
193 unbash_spaces(vol.VolCatName);
194 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
195 dcr->VolCatInfo = vol; /* structure assignment */
197 Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n",
198 vol.Slot, vol.VolCatName);
204 * Get Volume info for a specific volume from the Director's Database
206 * Returns: true on success (Director guarantees that Pool and MediaType
207 * are correct and VolStatus==Append or
208 * VolStatus==Recycle)
211 * Volume information returned in dcr->VolCatInfo
213 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
216 BSOCK *dir = jcr->dir_bsock;
218 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
219 bash_spaces(dcr->VolCatInfo.VolCatName);
220 bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
221 writing==GET_VOL_INFO_FOR_WRITE?1:0);
222 Dmsg1(100, ">dird: %s", dir->msg);
223 bool ok = do_get_volume_info(dcr);
230 * Get info on the next appendable volume in the Director's database
231 * Returns: true on success
234 * Volume information returned in dcr
237 bool dir_find_next_appendable_volume(DCR *dcr)
240 BSOCK *dir = jcr->dir_bsock;
243 Dmsg0(200, "dir_find_next_appendable_volume\n");
245 * Try the twenty oldest or most available volumes. Note,
246 * the most available could already be mounted on another
247 * drive, so we continue looking for a not in use Volume.
250 for (int vol_index=1; vol_index < 20; vol_index++) {
251 bash_spaces(dcr->media_type);
252 bash_spaces(dcr->pool_name);
253 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
254 unbash_spaces(dcr->media_type);
255 unbash_spaces(dcr->pool_name);
256 Dmsg1(100, ">dird: %s", dir->msg);
257 bool ok = do_get_volume_info(dcr);
259 if (dcr->any_volume || !is_volume_in_use(dcr)) {
263 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
267 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
268 dcr->dev->print_name());
274 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
275 new_volume(dcr, dcr->VolumeName); /* reserve volume */
276 unlock_reservations();
279 dcr->VolumeName[0] = 0;
280 unlock_reservations();
286 * After writing a Volume, send the updated statistics
287 * back to the director. The information comes from the
290 bool dir_update_volume_info(DCR *dcr, bool label)
293 BSOCK *dir = jcr->dir_bsock;
294 DEVICE *dev = dcr->dev;
295 time_t LastWritten = time(NULL);
296 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
297 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
301 /* If system job, do not update catalog */
302 if (jcr->JobType == JT_SYSTEM) {
306 if (vol->VolCatName[0] == 0) {
307 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
308 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
311 if (dev->can_read()) {
312 Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
313 Pmsg0(000, _("Attempt to update_volume_info in read mode!!!\n"));
317 Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
318 /* Just labeled or relabeled the tape */
320 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
322 pm_strcpy(VolumeName, vol->VolCatName);
323 bash_spaces(VolumeName);
324 InChanger = vol->InChanger;
325 bnet_fsend(dir, Update_media, jcr->Job,
326 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
327 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
328 vol->VolCatMounts, vol->VolCatErrors,
329 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
330 LastWritten, vol->VolCatStatus, vol->Slot, label,
331 InChanger, /* bool in structure */
332 edit_uint64(vol->VolReadTime, ed3),
333 edit_uint64(vol->VolWriteTime, ed4),
334 edit_uint64(vol->VolFirstWritten, ed5),
336 Dmsg1(100, ">dird: %s", dir->msg);
338 /* Do not lock device here because it may be locked from label */
339 if (!do_get_volume_info(dcr)) {
340 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
341 Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"),
342 vol->VolCatName, jcr->errmsg);
345 Dmsg1(420, "get_volume_info(): %s", dir->msg);
346 /* Update dev Volume info in case something changed (e.g. expired) */
347 dev->VolCatInfo = dcr->VolCatInfo;
352 * After writing a Volume, create the JobMedia record.
354 bool dir_create_jobmedia_record(DCR *dcr)
357 BSOCK *dir = jcr->dir_bsock;
360 /* If system job, do not update catalog */
361 if (jcr->JobType == JT_SYSTEM) {
365 if (!dcr->WroteVol) {
366 return true; /* nothing written to tape */
369 dcr->WroteVol = false;
370 bnet_fsend(dir, Create_job_media, jcr->Job,
371 dcr->VolFirstIndex, dcr->VolLastIndex,
372 dcr->StartFile, dcr->EndFile,
373 dcr->StartBlock, dcr->EndBlock,
374 dcr->Copy, dcr->Stripe,
375 edit_uint64(dcr->dev->VolCatInfo.VolMediaId, ed1));
376 Dmsg1(100, ">dird: %s", dir->msg);
377 if (bnet_recv(dir) <= 0) {
378 Dmsg0(190, "create_jobmedia error bnet_recv\n");
379 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
383 Dmsg1(100, "<dir: %s", dir->msg);
384 if (strcmp(dir->msg, OK_create) != 0) {
385 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
386 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
394 * Update File Attribute data
396 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
399 BSOCK *dir = jcr->dir_bsock;
402 #ifdef NO_ATTRIBUTES_TEST
406 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
407 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
408 sizeof(DEV_RECORD) + rec->data_len);
409 ser_begin(dir->msg + dir->msglen, 0);
410 ser_uint32(rec->VolSessionId);
411 ser_uint32(rec->VolSessionTime);
412 ser_int32(rec->FileIndex);
413 ser_int32(rec->Stream);
414 ser_uint32(rec->data_len);
415 ser_bytes(rec->data, rec->data_len);
416 dir->msglen = ser_length(dir->msg);
417 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
418 return bnet_send(dir);
423 * Request the sysop to create an appendable volume
425 * Entered with device blocked.
426 * Leaves with device blocked.
428 * Returns: true on success (operator issues a mount command)
430 * Note, must create dev->errmsg on error return.
432 * On success, dcr->VolumeName and dcr->VolCatInfo contain
433 * information on suggested volume, but this may not be the
434 * same as what is actually mounted.
436 * When we return with success, the correct tape may or may not
437 * actually be mounted. The calling routine must read it and
440 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
442 int stat = W_TIMEOUT;
443 DEVICE *dev = dcr->dev;
445 bool got_vol = false;
447 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
448 ASSERT(dev->dev_blocked);
450 if (job_canceled(jcr)) {
452 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
453 jcr->Job, dev->print_name());
454 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
458 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
463 if (stat == W_TIMEOUT || stat == W_MOUNT) {
464 Jmsg(jcr, M_MOUNT, 0, _(
465 "Job %s waiting. Cannot find any appendable volumes.\n"
466 "Please use the \"label\" command to create a new Volume for:\n"
477 set_jcr_job_status(jcr, JS_WaitMedia);
478 dir_send_job_status(jcr);
480 stat = wait_for_sysop(dcr);
481 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
483 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
487 if (stat == W_TIMEOUT) {
488 if (!double_dev_wait_time(dev)) {
489 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
490 dev->print_name(), jcr->Job);
491 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
492 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
493 return false; /* exceeded maximum waits */
497 if (stat == W_ERROR) {
499 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
500 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
503 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
505 set_jcr_job_status(jcr, JS_Running);
506 dir_send_job_status(jcr);
507 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
512 * Request to mount specific Volume
514 * Entered with device blocked and dcr->VolumeName is desired
516 * Leaves with device blocked.
518 * Returns: true on success (operator issues a mount command)
520 * Note, must create dev->errmsg on error return.
523 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
525 int stat = W_TIMEOUT;
526 DEVICE *dev = dcr->dev;
529 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
530 if (!dcr->VolumeName[0]) {
531 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
534 ASSERT(dev->dev_blocked);
536 if (job_canceled(jcr)) {
537 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
538 jcr->Job, dev->print_name());
547 * If we are not polling, and the wait timeout or the
548 * user explicitly did a mount, send him the message.
551 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
552 Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
553 dcr->VolumeName, dev->print_name(), jcr->Job);
554 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
555 dcr->VolumeName, dev->print_name(), jcr->Job);
558 set_jcr_job_status(jcr, JS_WaitMount);
559 dir_send_job_status(jcr);
561 stat = wait_for_sysop(dcr); /* wait on device */
562 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
564 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
565 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
569 if (stat == W_TIMEOUT) {
570 if (!double_dev_wait_time(dev)) {
571 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
572 dev->print_name(), jcr->Job);
573 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
574 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
575 return false; /* exceeded maximum waits */
579 if (stat == W_ERROR) {
581 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
582 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
585 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
588 set_jcr_job_status(jcr, JS_Running);
589 dir_send_job_status(jcr);
590 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");