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 amended 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.VolumeName);
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 /* ***FIXME*** we really should not do this but must for the moment */
187 if (dcr->dev->num_parts < dcr->VolCatInfo.VolCatParts) {
188 dcr->dev->num_parts = dcr->VolCatInfo.VolCatParts;
191 Dmsg2(300, "do_reqest_vol_info got slot=%d Volume=%s\n",
192 vol.Slot, vol.VolCatName);
198 * Get Volume info for a specific volume from the Director's Database
200 * Returns: true on success (Director guarantees that Pool and MediaType
201 * are correct and VolStatus==Append or
202 * VolStatus==Recycle)
205 * Volume information returned in dcr->VolCatInfo
207 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
210 BSOCK *dir = jcr->dir_bsock;
212 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
213 bash_spaces(dcr->VolCatInfo.VolCatName);
214 bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
215 writing==GET_VOL_INFO_FOR_WRITE?1:0);
216 Dmsg1(100, ">dird: %s", dir->msg);
217 return do_get_volume_info(dcr);
221 * Get info on the next appendable volume in the Director's database
222 * Returns: true on success
225 * Volume information returned in dcr
228 bool dir_find_next_appendable_volume(DCR *dcr)
231 BSOCK *dir = jcr->dir_bsock;
234 Dmsg0(200, "dir_find_next_appendable_volume\n");
236 * Try the twenty oldest or most available volumes. Note,
237 * the most available could already be mounted on another
238 * drive, so we continue looking for a not in use Volume.
240 for (int vol_index=1; vol_index < 20; vol_index++) {
241 bash_spaces(dcr->media_type);
242 bash_spaces(dcr->pool_name);
243 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
244 unbash_spaces(dcr->media_type);
245 unbash_spaces(dcr->pool_name);
246 Dmsg1(100, ">dird: %s", dir->msg);
247 if (do_get_volume_info(dcr)) {
248 if (is_volume_in_use(dcr->VolumeName)) {
255 Dmsg0(200, "No volume info, return false\n");
260 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
261 new_volume(dcr, dcr->VolumeName); /* reserve volume */
264 dcr->VolumeName[0] = 0;
270 * After writing a Volume, send the updated statistics
271 * back to the director. The information comes from the
274 bool dir_update_volume_info(DCR *dcr, bool label)
277 BSOCK *dir = jcr->dir_bsock;
278 DEVICE *dev = dcr->dev;
279 time_t LastWritten = time(NULL);
280 char ed1[50], ed2[50], ed3[50], ed4[50];
281 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
285 if (vol->VolCatName[0] == 0) {
286 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
287 Pmsg0(000, "NULL Volume name. This shouldn't happen!!!\n");
290 if (dev->can_read()) {
291 Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
292 Pmsg0(000, "Attempt to update_volume_info in read mode!!!\n");
296 Dmsg1(300, "Update cat VolFiles=%d\n", dev->file);
297 /* Just labeled or relabeled the tape */
299 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
300 vol->VolCatBytes = 1; /* indicates tape labeled */
302 pm_strcpy(VolumeName, vol->VolCatName);
303 bash_spaces(VolumeName);
304 InChanger = vol->InChanger;
305 bnet_fsend(dir, Update_media, jcr->Job,
306 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
307 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
308 vol->VolCatMounts, vol->VolCatErrors,
309 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
310 LastWritten, vol->VolCatStatus, vol->Slot, label,
311 InChanger, /* bool in structure */
312 edit_uint64(vol->VolReadTime, ed3),
313 edit_uint64(vol->VolWriteTime, ed4),
315 Dmsg1(100, ">dird: %s", dir->msg);
317 if (!do_get_volume_info(dcr)) {
318 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
319 Pmsg2(000, "Didn't get vol info vol=%s: ERR=%s",
320 vol->VolCatName, jcr->errmsg);
323 Dmsg1(420, "get_volume_info(): %s", dir->msg);
324 /* Update dev Volume info in case something changed (e.g. expired) */
325 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
330 * After writing a Volume, create the JobMedia record.
332 bool dir_create_jobmedia_record(DCR *dcr)
335 BSOCK *dir = jcr->dir_bsock;
337 if (!dcr->WroteVol) {
338 return true; /* nothing written to tape */
341 dcr->WroteVol = false;
342 bnet_fsend(dir, Create_job_media, jcr->Job,
343 dcr->VolFirstIndex, dcr->VolLastIndex,
344 dcr->StartFile, dcr->EndFile,
345 dcr->StartBlock, dcr->EndBlock,
346 dcr->Copy, dcr->Stripe);
347 Dmsg1(100, ">dird: %s", dir->msg);
348 if (bnet_recv(dir) <= 0) {
349 Dmsg0(190, "create_jobmedia error bnet_recv\n");
350 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
354 Dmsg1(100, "<dir: %s", dir->msg);
355 if (strcmp(dir->msg, OK_create) != 0) {
356 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
357 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
365 * Update File Attribute data
367 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
370 BSOCK *dir = jcr->dir_bsock;
373 #ifdef NO_ATTRIBUTES_TEST
377 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
378 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
379 sizeof(DEV_RECORD) + rec->data_len);
380 ser_begin(dir->msg + dir->msglen, 0);
381 ser_uint32(rec->VolSessionId);
382 ser_uint32(rec->VolSessionTime);
383 ser_int32(rec->FileIndex);
384 ser_int32(rec->Stream);
385 ser_uint32(rec->data_len);
386 ser_bytes(rec->data, rec->data_len);
387 dir->msglen = ser_length(dir->msg);
388 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
389 return bnet_send(dir);
394 * Request the sysop to create an appendable volume
396 * Entered with device blocked.
397 * Leaves with device blocked.
399 * Returns: true on success (operator issues a mount command)
401 * Note, must create dev->errmsg on error return.
403 * On success, dcr->VolumeName and dcr->VolCatInfo contain
404 * information on suggested volume, but this may not be the
405 * same as what is actually mounted.
407 * When we return with success, the correct tape may or may not
408 * actually be mounted. The calling routine must read it and
411 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
416 DEVICE *dev = dcr->dev;
419 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
420 ASSERT(dev->dev_blocked);
422 if (job_canceled(jcr)) {
424 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
425 jcr->Job, dev->print_name());
426 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
429 /* First pass, we *know* there are no appendable volumes, so no need to call */
430 if (!first && dir_find_next_appendable_volume(dcr)) { /* get suggested volume */
431 unmounted = is_device_unmounted(dev);
433 * If we have a valid volume name and we are not
434 * removable media, return now, or if we have a
435 * Slot for an autochanger, otherwise wait
436 * for the operator to mount the media.
438 if (!unmounted && ((dcr->VolumeName[0] && !dev_cap(dev, CAP_REM) &&
439 dev_cap(dev, CAP_LABEL)) ||
440 (dcr->VolumeName[0] && dcr->VolCatInfo.Slot))) {
441 Dmsg0(400, "Return 1 from mount without wait.\n");
444 jstat = JS_WaitMount;
446 Jmsg(jcr, M_MOUNT, 0, _(
447 "Please mount Volume \"%s\" on Storage Device %s for Job %s\n"
448 "Use \"mount\" command to release Job.\n"),
449 dcr->VolumeName, dev->print_name(), jcr->Job);
450 Dmsg3(400, "Mount %s on %s for Job %s\n",
451 dcr->VolumeName, dcr->dev_name, jcr->Job);
454 jstat = JS_WaitMedia;
456 Jmsg(jcr, M_MOUNT, 0, _(
457 "Job %s waiting. Cannot find any appendable volumes.\n"
458 "Please use the \"label\" command to create a new Volume for:\n"
470 jcr->JobStatus = jstat;
471 dir_send_job_status(jcr);
473 stat = wait_for_sysop(dcr);
475 Dmsg1(400, "Poll timeout in create append vol on device %s\n", dev->print_name());
479 if (stat == ETIMEDOUT) {
480 if (!double_dev_wait_time(dev)) {
481 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
482 dev->print_name(), jcr->Job);
483 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
484 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
485 return false; /* exceeded maximum waits */
489 if (stat == EINVAL) {
491 Mmsg2(dev->errmsg, _("pthread error in mount_next_volume stat=%d ERR=%s\n"),
492 stat, be.strerror(stat));
493 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
498 Jmsg(jcr, M_WARNING, 0, _("pthread error in mount_next_volume stat=%d ERR=%s\n"), stat,
501 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
503 /* If no VolumeName, and cannot get one, try again */
504 if (dcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
505 !dir_find_next_appendable_volume(dcr)) {
506 Jmsg(jcr, M_MOUNT, 0, _(
507 "Someone woke me up, but I cannot find any appendable\n"
508 "volumes for Job=%s.\n"), jcr->Job);
509 /* Restart wait counters after user interaction */
510 init_device_wait_timers(dcr);
513 unmounted = is_device_unmounted(dev);
515 continue; /* continue to wait */
519 * Device mounted, we have a volume, break and return
523 set_jcr_job_status(jcr, JS_Running);
524 dir_send_job_status(jcr);
525 Dmsg0(400, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
530 * Request to mount specific Volume
532 * Entered with device blocked and dcr->VolumeName is desired
534 * Leaves with device blocked.
536 * Returns: true on success (operator issues a mount command)
538 * Note, must create dev->errmsg on error return.
541 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
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());
562 msg = _("Please mount");
563 Jmsg(jcr, M_MOUNT, 0, _("%s Volume \"%s\" on Storage Device %s for Job %s\n"),
564 msg, dcr->VolumeName, dev->print_name(), jcr->Job);
565 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
566 dcr->VolumeName, dcr->dev_name, jcr->Job);
569 jcr->JobStatus = JS_WaitMount;
570 dir_send_job_status(jcr);
572 stat = wait_for_sysop(dcr); ; /* wait on device */
574 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
575 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
579 if (stat == ETIMEDOUT) {
580 if (!double_dev_wait_time(dev)) {
581 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
582 dev->print_name(), jcr->Job);
583 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
584 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
585 return false; /* exceeded maximum waits */
589 if (stat == EINVAL) {
591 Mmsg2(dev->errmsg, _("pthread error in mount_volume stat=%d ERR=%s\n"),
592 stat, be.strerror(stat));
593 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
598 Jmsg(jcr, M_FATAL, 0, _("pthread error in mount_next_volume stat=%d: ERR=%s\n"), stat,
601 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
604 set_jcr_job_status(jcr, JS_Running);
605 dir_send_job_status(jcr);
606 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");