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 Copyright (C) 2000-2005 Kern Sibbald
12 This program is free software; you can redistribute it and/or
13 modify it under the terms of the GNU General Public License
14 version 2 as ammended with additional clauses defined in the
15 file LICENSE in the main source directory.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 the file LICENSE for additional details.
24 #include "bacula.h" /* pull in global headers */
25 #include "stored.h" /* pull in Storage Deamon headers */
27 /* Requests sent to the Director */
28 static char Find_media[] = "CatReq Job=%s FindMedia=%d pool_name=%s media_type=%s\n";
29 static char Get_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s write=%d\n";
30 static char Update_media[] = "CatReq Job=%s UpdateMedia VolName=%s"
31 " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%s VolMounts=%u"
32 " VolErrors=%u VolWrites=%u MaxVolBytes=%s EndTime=%d VolStatus=%s"
33 " Slot=%d relabel=%d InChanger=%d VolReadTime=%s VolWriteTime=%s"
35 static char Create_job_media[] = "CatReq Job=%s CreateJobMedia"
36 " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u"
37 " StartBlock=%u EndBlock=%u Copy=%d Strip=%d\n";
38 static char FileAttributes[] = "UpdCat Job=%s FileAttributes ";
39 static char Job_status[] = "Status Job=%s JobStatus=%d\n";
43 /* Responses received from the Director */
44 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
45 " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
46 " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
47 " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
48 " VolReadTime=%" lld " VolWriteTime=%" lld " EndFile=%u EndBlock=%u"
49 " VolParts=%u LabelType=%d";
52 static char OK_create[] = "1000 OK CreateJobMedia\n";
56 static char Device_update[] = "DevUpd Job=%s device=%s "
57 "append=%d read=%d num_writers=%d "
58 "open=%d labeled=%d offline=%d "
59 "reserved=%d max_writers=%d "
60 "autoselect=%d autochanger=%d "
61 "changer_name=%s media_type=%s volume_name=%s\n";
64 /* Send update information about a device to Director */
65 bool dir_update_device(JCR *jcr, DEVICE *dev)
67 BSOCK *dir = jcr->dir_bsock;
68 POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
69 DEVRES *device = dev->device;
72 pm_strcpy(dev_name, device->hdr.name);
73 bash_spaces(dev_name);
74 if (dev->is_labeled()) {
75 pm_strcpy(VolumeName, dev->VolHdr.VolName);
77 pm_strcpy(VolumeName, "*");
79 bash_spaces(VolumeName);
80 pm_strcpy(MediaType, device->media_type);
81 bash_spaces(MediaType);
82 if (device->changer_res) {
83 pm_strcpy(ChangerName, device->changer_res->hdr.name);
84 bash_spaces(ChangerName);
86 pm_strcpy(ChangerName, "*");
88 ok =bnet_fsend(dir, Device_update,
92 dev->can_read()!=0, dev->num_writers,
93 dev->is_open()!=0, dev->is_labeled()!=0,
94 dev->is_offline()!=0, dev->reserved_device,
95 dev->is_tape()?100000:1,
97 ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
98 Dmsg1(100, ">dird: %s\n", dir->msg);
102 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
104 BSOCK *dir = jcr->dir_bsock;
105 POOL_MEM dev_name, MediaType;
109 pm_strcpy(dev_name, changer->hdr.name);
110 bash_spaces(dev_name);
111 device = (DEVRES *)changer->device->first();
112 pm_strcpy(MediaType, device->media_type);
113 bash_spaces(MediaType);
114 /* This is mostly to indicate that we are here */
115 ok = bnet_fsend(dir, Device_update,
117 dev_name.c_str(), /* Changer name */
118 0, 0, 0, /* append, read, num_writers */
119 0, 0, 0, /* is_open, is_labeled, offline */
120 0, 0, /* reserved, max_writers */
122 changer->device->size(), /* Number of devices */
124 "*", /* ChangerName */
125 MediaType.c_str(), /* MediaType */
127 Dmsg1(100, ">dird: %s\n", dir->msg);
134 * Send current JobStatus to Director
136 bool dir_send_job_status(JCR *jcr)
138 return bnet_fsend(jcr->dir_bsock, Job_status, jcr->Job, jcr->JobStatus);
142 * Common routine for:
143 * dir_get_volume_info()
145 * dir_find_next_appendable_volume()
147 * Returns: true on success and vol info in dcr->VolCatInfo
150 static bool do_get_volume_info(DCR *dcr)
153 BSOCK *dir = jcr->dir_bsock;
158 dcr->VolumeName[0] = 0; /* No volume */
159 if (bnet_recv(dir) <= 0) {
160 Dmsg0(200, "getvolname error bnet_recv\n");
161 Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
164 memset(&vol, 0, sizeof(vol));
165 Dmsg1(100, "<dird %s", dir->msg);
166 n = sscanf(dir->msg, OK_media, vol.VolCatName,
167 &vol.VolCatJobs, &vol.VolCatFiles,
168 &vol.VolCatBlocks, &vol.VolCatBytes,
169 &vol.VolCatMounts, &vol.VolCatErrors,
170 &vol.VolCatWrites, &vol.VolCatMaxBytes,
171 &vol.VolCatCapacityBytes, vol.VolCatStatus,
172 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
173 &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
174 &vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
177 Dmsg2(100, "Bad response from Dir fields=%d: %s\n", n, dir->msg);
178 Mmsg(jcr->errmsg, _("Error getting Volume info: %s\n"), dir->msg);
181 vol.InChanger = InChanger; /* bool in structure */
182 unbash_spaces(vol.VolCatName);
183 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
184 memcpy(&dcr->VolCatInfo, &vol, sizeof(dcr->VolCatInfo));
186 Dmsg2(300, "do_reqest_vol_info got slot=%d Volume=%s\n",
187 vol.Slot, vol.VolCatName);
193 * Get Volume info for a specific volume from the Director's Database
195 * Returns: true on success (not Director guarantees that Pool and MediaType
196 * are correct and VolStatus==Append or
197 * VolStatus==Recycle)
200 * Volume information returned in dcr->VolCatInfo
202 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
205 BSOCK *dir = jcr->dir_bsock;
207 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
208 bash_spaces(dcr->VolCatInfo.VolCatName);
209 bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
210 writing==GET_VOL_INFO_FOR_WRITE?1:0);
211 Dmsg1(100, ">dird: %s", dir->msg);
212 return do_get_volume_info(dcr);
216 * Rewrite this to use a list, but need something to add
217 * and remove volumes from the list.
219 static bool is_volume_in_use(JCR *jcr, const char *VolumeName) {
222 Dmsg2(300, "JobId=%d got possible Vol=%s\n", jcr->JobId, VolumeName);
224 * Walk through all jobs and see if the volume is
225 * already mounted. If so, try a different one.
226 * This would be better done by walking through
234 Dmsg2(300, "Compare to JobId=%d using Vol=%s\n", njcr->JobId, njcr->dcr->VolumeName);
235 if (njcr->dcr && strcmp(VolumeName, njcr->dcr->VolumeName) == 0) {
237 Dmsg1(400, "Vol in use by JobId=%u\n", njcr->JobId);
249 * Get info on the next appendable volume in the Director's database
250 * Returns: true on success
253 * Volume information returned in dcr
256 bool dir_find_next_appendable_volume(DCR *dcr)
259 BSOCK *dir = jcr->dir_bsock;
261 Dmsg0(200, "dir_find_next_appendable_volume\n");
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.
267 for (int vol_index=1; vol_index < 20; vol_index++) {
268 bash_spaces(dcr->media_type);
269 bash_spaces(dcr->pool_name);
270 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
271 unbash_spaces(dcr->media_type);
272 unbash_spaces(dcr->pool_name);
273 Dmsg1(100, ">dird: %s", dir->msg);
274 if (do_get_volume_info(dcr)) {
275 if (is_volume_in_use(jcr, dcr->VolumeName)) {
281 Dmsg0(200, "No volume info, return false\n");
285 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
291 * After writing a Volume, send the updated statistics
292 * back to the director. The information comes from the
295 bool dir_update_volume_info(DCR *dcr, bool label)
298 BSOCK *dir = jcr->dir_bsock;
299 DEVICE *dev = dcr->dev;
300 time_t LastWritten = time(NULL);
301 char ed1[50], ed2[50], ed3[50], ed4[50];
302 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
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(300, "Update cat VolFiles=%d\n", dev->file);
318 /* Just labeled or relabeled the tape */
320 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
321 vol->VolCatBytes = 1; /* indicates tape labeled */
323 pm_strcpy(VolumeName, vol->VolCatName);
324 bash_spaces(VolumeName);
325 InChanger = vol->InChanger;
326 bnet_fsend(dir, Update_media, jcr->Job,
327 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
328 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
329 vol->VolCatMounts, vol->VolCatErrors,
330 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
331 LastWritten, vol->VolCatStatus, vol->Slot, label,
332 InChanger, /* bool in structure */
333 edit_uint64(vol->VolReadTime, ed3),
334 edit_uint64(vol->VolWriteTime, ed4),
336 Dmsg1(100, ">dird: %s", dir->msg);
338 if (!do_get_volume_info(dcr)) {
339 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
340 Pmsg2(000, "Didn't get vol info vol=%s: ERR=%s",
341 vol->VolCatName, jcr->errmsg);
344 Dmsg1(420, "get_volume_info(): %s", dir->msg);
345 /* Update dev Volume info in case something changed (e.g. expired) */
346 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
351 * After writing a Volume, create the JobMedia record.
353 bool dir_create_jobmedia_record(DCR *dcr)
356 BSOCK *dir = jcr->dir_bsock;
358 if (!dcr->WroteVol) {
359 return true; /* nothing written to tape */
362 dcr->WroteVol = false;
363 bnet_fsend(dir, Create_job_media, jcr->Job,
364 dcr->VolFirstIndex, dcr->VolLastIndex,
365 dcr->StartFile, dcr->EndFile,
366 dcr->StartBlock, dcr->EndBlock,
367 dcr->Copy, dcr->Stripe);
368 Dmsg1(100, ">dird: %s", dir->msg);
369 if (bnet_recv(dir) <= 0) {
370 Dmsg0(190, "create_jobmedia error bnet_recv\n");
371 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
375 Dmsg1(100, "<dir: %s", dir->msg);
376 if (strcmp(dir->msg, OK_create) != 0) {
377 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
378 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
386 * Update File Attribute data
388 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
391 BSOCK *dir = jcr->dir_bsock;
394 #ifdef NO_ATTRIBUTES_TEST
398 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
399 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
400 sizeof(DEV_RECORD) + rec->data_len);
401 ser_begin(dir->msg + dir->msglen, 0);
402 ser_uint32(rec->VolSessionId);
403 ser_uint32(rec->VolSessionTime);
404 ser_int32(rec->FileIndex);
405 ser_int32(rec->Stream);
406 ser_uint32(rec->data_len);
407 ser_bytes(rec->data, rec->data_len);
408 dir->msglen = ser_length(dir->msg);
409 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
410 return bnet_send(dir);
415 * Request the sysop to create an appendable volume
417 * Entered with device blocked.
418 * Leaves with device blocked.
420 * Returns: true on success (operator issues a mount command)
422 * Note, must create dev->errmsg on error return.
424 * On success, dcr->VolumeName and dcr->VolCatInfo contain
425 * information on suggested volume, but this may not be the
426 * same as what is actually mounted.
428 * When we return with success, the correct tape may or may not
429 * actually be mounted. The calling routine must read it and
432 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
437 DEVICE *dev = dcr->dev;
440 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
441 ASSERT(dev->dev_blocked);
443 if (job_canceled(jcr)) {
445 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
446 jcr->Job, dev->print_name());
447 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
450 /* First pass, we *know* there are no appendable volumes, so no need to call */
451 if (!first && dir_find_next_appendable_volume(dcr)) { /* get suggested volume */
452 unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
453 (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
455 * If we have a valid volume name and we are not
456 * removable media, return now, or if we have a
457 * Slot for an autochanger, otherwise wait
458 * for the operator to mount the media.
460 if (!unmounted && ((dcr->VolumeName[0] && !dev_cap(dev, CAP_REM) &&
461 dev_cap(dev, CAP_LABEL)) ||
462 (dcr->VolumeName[0] && dcr->VolCatInfo.Slot))) {
463 Dmsg0(400, "Return 1 from mount without wait.\n");
466 jstat = JS_WaitMount;
468 Jmsg(jcr, M_MOUNT, 0, _(
469 "Please mount Volume \"%s\" on Storage Device %s for Job %s\n"
470 "Use \"mount\" command to release Job.\n"),
471 dcr->VolumeName, dev->print_name(), jcr->Job);
472 Dmsg3(400, "Mount %s on %s for Job %s\n",
473 dcr->VolumeName, dcr->dev_name, jcr->Job);
476 jstat = JS_WaitMedia;
478 Jmsg(jcr, M_MOUNT, 0, _(
479 "Job %s waiting. Cannot find any appendable volumes.\n"
480 "Please use the \"label\" command to create a new Volume for:\n"
492 jcr->JobStatus = jstat;
493 dir_send_job_status(jcr);
495 stat = wait_for_sysop(dcr);
497 Dmsg1(400, "Poll timeout in create append vol on device %s\n", dev->print_name());
501 if (stat == ETIMEDOUT) {
502 if (!double_dev_wait_time(dev)) {
503 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
504 dev->print_name(), jcr->Job);
505 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
506 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
507 return false; /* exceeded maximum waits */
511 if (stat == EINVAL) {
513 Mmsg2(dev->errmsg, _("pthread error in mount_next_volume stat=%d ERR=%s\n"),
514 stat, be.strerror(stat));
515 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
520 Jmsg(jcr, M_WARNING, 0, _("pthread error in mount_next_volume stat=%d ERR=%s\n"), stat,
523 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
525 /* If no VolumeName, and cannot get one, try again */
526 if (dcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
527 !dir_find_next_appendable_volume(dcr)) {
528 Jmsg(jcr, M_MOUNT, 0, _(
529 "Someone woke me up, but I cannot find any appendable\n"
530 "volumes for Job=%s.\n"), jcr->Job);
531 /* Restart wait counters after user interaction */
532 init_device_wait_timers(dcr);
535 unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
536 (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
538 continue; /* continue to wait */
542 * Device mounted, we have a volume, break and return
546 set_jcr_job_status(jcr, JS_Running);
547 dir_send_job_status(jcr);
548 Dmsg0(400, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
553 * Request to mount specific Volume
555 * Entered with device blocked and dcr->VolumeName is desired
557 * Leaves with device blocked.
559 * Returns: true on success (operator issues a mount command)
561 * Note, must create dev->errmsg on error return.
564 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
568 DEVICE *dev = dcr->dev;
571 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
572 if (!dcr->VolumeName[0]) {
573 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
576 ASSERT(dev->dev_blocked);
578 if (job_canceled(jcr)) {
579 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
580 jcr->Job, dev->print_name());
585 msg = _("Please mount");
586 Jmsg(jcr, M_MOUNT, 0, _("%s Volume \"%s\" on Storage Device %s for Job %s\n"),
587 msg, dcr->VolumeName, dev->print_name(), jcr->Job);
588 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
589 dcr->VolumeName, dcr->dev_name, jcr->Job);
592 jcr->JobStatus = JS_WaitMount;
593 dir_send_job_status(jcr);
595 stat = wait_for_sysop(dcr); ; /* wait on device */
597 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
598 Dmsg1(400, "Blocked=%s\n", edit_blocked_reason(dev));
602 if (stat == ETIMEDOUT) {
603 if (!double_dev_wait_time(dev)) {
604 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
605 dev->print_name(), jcr->Job);
606 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
607 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
608 return false; /* exceeded maximum waits */
612 if (stat == EINVAL) {
614 Mmsg2(dev->errmsg, _("pthread error in mount_volume stat=%d ERR=%s\n"),
615 stat, be.strerror(stat));
616 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
621 Jmsg(jcr, M_FATAL, 0, _("pthread error in mount_next_volume stat=%d: ERR=%s\n"), stat,
624 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
627 set_jcr_job_status(jcr, JS_Running);
628 dir_send_job_status(jcr);
629 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");