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;
190 Now done in dev.c:open_dvd_device
193 Dmsg2(300, "do_reqest_vol_info return true slot=%d Volume=%s\n",
194 vol.Slot, vol.VolCatName);
200 * Get Volume info for a specific volume from the Director's Database
202 * Returns: true on success (Director guarantees that Pool and MediaType
203 * are correct and VolStatus==Append or
204 * VolStatus==Recycle)
207 * Volume information returned in dcr->VolCatInfo
209 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
212 BSOCK *dir = jcr->dir_bsock;
214 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
215 bash_spaces(dcr->VolCatInfo.VolCatName);
216 bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
217 writing==GET_VOL_INFO_FOR_WRITE?1:0);
218 Dmsg1(100, ">dird: %s", dir->msg);
219 bool OK = do_get_volume_info(dcr);
224 * Get info on the next appendable volume in the Director's database
225 * Returns: true on success
228 * Volume information returned in dcr
231 bool dir_find_next_appendable_volume(DCR *dcr)
234 BSOCK *dir = jcr->dir_bsock;
237 Dmsg0(200, "dir_find_next_appendable_volume\n");
239 * Try the twenty oldest or most available volumes. Note,
240 * the most available could already be mounted on another
241 * drive, so we continue looking for a not in use Volume.
243 for (int vol_index=1; vol_index < 20; vol_index++) {
244 bash_spaces(dcr->media_type);
245 bash_spaces(dcr->pool_name);
246 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
247 unbash_spaces(dcr->media_type);
248 unbash_spaces(dcr->pool_name);
249 Dmsg1(100, ">dird: %s", dir->msg);
250 bool OK = do_get_volume_info(dcr);
252 if (is_volume_in_use(dcr)) {
253 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
260 Dmsg0(200, "No volume info, return false\n");
265 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
266 new_volume(dcr, dcr->VolumeName); /* reserve volume */
269 dcr->VolumeName[0] = 0;
275 * After writing a Volume, send the updated statistics
276 * back to the director. The information comes from the
279 bool dir_update_volume_info(DCR *dcr, bool label)
282 BSOCK *dir = jcr->dir_bsock;
283 DEVICE *dev = dcr->dev;
284 time_t LastWritten = time(NULL);
285 char ed1[50], ed2[50], ed3[50], ed4[50];
286 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
290 if (vol->VolCatName[0] == 0) {
291 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
292 Pmsg0(000, "NULL Volume name. This shouldn't happen!!!\n");
295 if (dev->can_read()) {
296 Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
297 Pmsg0(000, "Attempt to update_volume_info in read mode!!!\n");
301 Dmsg1(300, "Update cat VolFiles=%d\n", dev->file);
302 /* Just labeled or relabeled the tape */
304 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
305 vol->VolCatBytes = 1; /* indicates tape labeled */
307 pm_strcpy(VolumeName, vol->VolCatName);
308 bash_spaces(VolumeName);
309 InChanger = vol->InChanger;
310 bnet_fsend(dir, Update_media, jcr->Job,
311 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
312 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
313 vol->VolCatMounts, vol->VolCatErrors,
314 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
315 LastWritten, vol->VolCatStatus, vol->Slot, label,
316 InChanger, /* bool in structure */
317 edit_uint64(vol->VolReadTime, ed3),
318 edit_uint64(vol->VolWriteTime, ed4),
320 Dmsg1(100, ">dird: %s", dir->msg);
322 /* Do not lock device here because it may be locked from label */
323 if (!do_get_volume_info(dcr)) {
324 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
325 Pmsg2(000, "Didn't get vol info vol=%s: ERR=%s",
326 vol->VolCatName, jcr->errmsg);
329 Dmsg1(420, "get_volume_info(): %s", dir->msg);
330 /* Update dev Volume info in case something changed (e.g. expired) */
331 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
336 * After writing a Volume, create the JobMedia record.
338 bool dir_create_jobmedia_record(DCR *dcr)
341 BSOCK *dir = jcr->dir_bsock;
343 if (!dcr->WroteVol) {
344 return true; /* nothing written to tape */
347 dcr->WroteVol = false;
348 bnet_fsend(dir, Create_job_media, jcr->Job,
349 dcr->VolFirstIndex, dcr->VolLastIndex,
350 dcr->StartFile, dcr->EndFile,
351 dcr->StartBlock, dcr->EndBlock,
352 dcr->Copy, dcr->Stripe);
353 Dmsg1(100, ">dird: %s", dir->msg);
354 if (bnet_recv(dir) <= 0) {
355 Dmsg0(190, "create_jobmedia error bnet_recv\n");
356 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
360 Dmsg1(100, "<dir: %s", dir->msg);
361 if (strcmp(dir->msg, OK_create) != 0) {
362 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
363 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
371 * Update File Attribute data
373 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
376 BSOCK *dir = jcr->dir_bsock;
379 #ifdef NO_ATTRIBUTES_TEST
383 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
384 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
385 sizeof(DEV_RECORD) + rec->data_len);
386 ser_begin(dir->msg + dir->msglen, 0);
387 ser_uint32(rec->VolSessionId);
388 ser_uint32(rec->VolSessionTime);
389 ser_int32(rec->FileIndex);
390 ser_int32(rec->Stream);
391 ser_uint32(rec->data_len);
392 ser_bytes(rec->data, rec->data_len);
393 dir->msglen = ser_length(dir->msg);
394 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
395 return bnet_send(dir);
400 * Request the sysop to create an appendable volume
402 * Entered with device blocked.
403 * Leaves with device blocked.
405 * Returns: true on success (operator issues a mount command)
407 * Note, must create dev->errmsg on error return.
409 * On success, dcr->VolumeName and dcr->VolCatInfo contain
410 * information on suggested volume, but this may not be the
411 * same as what is actually mounted.
413 * When we return with success, the correct tape may or may not
414 * actually be mounted. The calling routine must read it and
417 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
422 DEVICE *dev = dcr->dev;
426 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
427 ASSERT(dev->dev_blocked);
429 if (job_canceled(jcr)) {
431 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
432 jcr->Job, dev->print_name());
433 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
436 /* First pass, we *know* there are no appendable volumes, so no need to call */
439 OK = dir_find_next_appendable_volume(dcr); /* get suggested volume */
443 unmounted = is_device_unmounted(dev);
445 * If we have a valid volume name and we are not
446 * removable media, return now, or if we have a
447 * Slot for an autochanger, otherwise wait
448 * for the operator to mount the media.
450 if (!unmounted && ((dcr->VolumeName[0] && !dev_cap(dev, CAP_REM) &&
451 dev_cap(dev, CAP_LABEL)) ||
452 (dcr->VolumeName[0] && dcr->VolCatInfo.Slot))) {
453 Dmsg0(400, "Return 1 from mount without wait.\n");
456 jstat = JS_WaitMount;
458 Jmsg(jcr, M_MOUNT, 0, _(
459 "Please mount Volume \"%s\" on Storage Device %s for Job %s\n"
460 "Use \"mount\" command to release Job.\n"),
461 dcr->VolumeName, dev->print_name(), jcr->Job);
462 Dmsg3(400, "Mount %s on %s for Job %s\n",
463 dcr->VolumeName, dcr->dev_name, jcr->Job);
466 jstat = JS_WaitMedia;
468 Jmsg(jcr, M_MOUNT, 0, _(
469 "Job %s waiting. Cannot find any appendable volumes.\n"
470 "Please use the \"label\" command to create a new Volume for:\n"
482 jcr->JobStatus = jstat;
483 dir_send_job_status(jcr);
485 stat = wait_for_sysop(dcr);
487 Dmsg1(400, "Poll timeout in create append vol on device %s\n", dev->print_name());
491 if (stat == ETIMEDOUT) {
492 if (!double_dev_wait_time(dev)) {
493 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
494 dev->print_name(), jcr->Job);
495 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
496 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
497 return false; /* exceeded maximum waits */
501 if (stat == EINVAL) {
503 Mmsg2(dev->errmsg, _("pthread error in mount_next_volume stat=%d ERR=%s\n"),
504 stat, be.strerror(stat));
505 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
510 Jmsg(jcr, M_WARNING, 0, _("pthread error in mount_next_volume stat=%d ERR=%s\n"), stat,
513 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
515 /* If no VolumeName, and cannot get one, try again */
517 if (dcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
518 !dir_find_next_appendable_volume(dcr)) {
520 Jmsg(jcr, M_MOUNT, 0, _(
521 "Someone woke me up, but I cannot find any appendable\n"
522 "volumes for Job=%s.\n"), jcr->Job);
523 /* Restart wait counters after user interaction */
524 init_device_wait_timers(dcr);
528 unmounted = is_device_unmounted(dev);
530 Dmsg0(400, "Device is unmounted. Must wait.\n");
531 continue; /* continue to wait */
535 * Device mounted, we have a volume, break and return
539 set_jcr_job_status(jcr, JS_Running);
540 dir_send_job_status(jcr);
541 Dmsg0(400, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
546 * Request to mount specific Volume
548 * Entered with device blocked and dcr->VolumeName is desired
550 * Leaves with device blocked.
552 * Returns: true on success (operator issues a mount command)
554 * Note, must create dev->errmsg on error return.
557 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
561 DEVICE *dev = dcr->dev;
564 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
565 if (!dcr->VolumeName[0]) {
566 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
569 ASSERT(dev->dev_blocked);
571 if (job_canceled(jcr)) {
572 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
573 jcr->Job, dev->print_name());
578 msg = _("Please mount");
579 Jmsg(jcr, M_MOUNT, 0, _("%s Volume \"%s\" on Storage Device %s for Job %s\n"),
580 msg, dcr->VolumeName, dev->print_name(), jcr->Job);
581 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
582 dcr->VolumeName, dev->print_name(), jcr->Job);
585 jcr->JobStatus = JS_WaitMount;
586 dir_send_job_status(jcr);
588 stat = wait_for_sysop(dcr); ; /* wait on device */
590 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
591 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
595 if (stat == ETIMEDOUT) {
596 if (!double_dev_wait_time(dev)) {
597 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
598 dev->print_name(), jcr->Job);
599 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
600 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
601 return false; /* exceeded maximum waits */
605 if (stat == EINVAL) {
607 Mmsg2(dev->errmsg, _("pthread error in mount_volume stat=%d ERR=%s\n"),
608 stat, be.strerror(stat));
609 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
614 Jmsg(jcr, M_FATAL, 0, _("pthread error in mount_next_volume stat=%d: ERR=%s\n"), stat,
617 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
620 set_jcr_job_status(jcr, JS_Running);
621 dir_send_job_status(jcr);
622 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");