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 Dmsg2(300, "do_reqest_vol_info return true 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 (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 bool OK = do_get_volume_info(dcr);
217 * Get info on the next appendable volume in the Director's database
218 * Returns: true on success
221 * Volume information returned in dcr
224 bool dir_find_next_appendable_volume(DCR *dcr)
227 BSOCK *dir = jcr->dir_bsock;
230 Dmsg0(200, "dir_find_next_appendable_volume\n");
232 * Try the twenty oldest or most available volumes. Note,
233 * the most available could already be mounted on another
234 * drive, so we continue looking for a not in use Volume.
236 for (int vol_index=1; vol_index < 20; vol_index++) {
237 bash_spaces(dcr->media_type);
238 bash_spaces(dcr->pool_name);
239 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
240 unbash_spaces(dcr->media_type);
241 unbash_spaces(dcr->pool_name);
242 Dmsg1(100, ">dird: %s", dir->msg);
243 bool OK = do_get_volume_info(dcr);
245 if (dcr->any_volume || !is_volume_in_use(dcr)) {
249 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
253 Dmsg0(200, "No volume info, return false\n");
258 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
259 new_volume(dcr, dcr->VolumeName); /* reserve volume */
262 dcr->VolumeName[0] = 0;
268 * After writing a Volume, send the updated statistics
269 * back to the director. The information comes from the
272 bool dir_update_volume_info(DCR *dcr, bool label)
275 BSOCK *dir = jcr->dir_bsock;
276 DEVICE *dev = dcr->dev;
277 time_t LastWritten = time(NULL);
278 char ed1[50], ed2[50], ed3[50], ed4[50];
279 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
283 if (vol->VolCatName[0] == 0) {
284 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
285 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
288 if (dev->can_read()) {
289 Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
290 Pmsg0(000, _("Attempt to update_volume_info in read mode!!!\n"));
294 Dmsg1(300, "Update cat VolFiles=%d\n", dev->file);
295 /* Just labeled or relabeled the tape */
297 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
298 vol->VolCatBytes = 1; /* indicates tape labeled */
300 pm_strcpy(VolumeName, vol->VolCatName);
301 bash_spaces(VolumeName);
302 InChanger = vol->InChanger;
303 bnet_fsend(dir, Update_media, jcr->Job,
304 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
305 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
306 vol->VolCatMounts, vol->VolCatErrors,
307 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
308 LastWritten, vol->VolCatStatus, vol->Slot, label,
309 InChanger, /* bool in structure */
310 edit_uint64(vol->VolReadTime, ed3),
311 edit_uint64(vol->VolWriteTime, ed4),
313 Dmsg1(100, ">dird: %s", dir->msg);
315 /* Do not lock device here because it may be locked from label */
316 if (!do_get_volume_info(dcr)) {
317 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
318 Pmsg2(000, _("Didn't get vol info vol=%s: ERR=%s"),
319 vol->VolCatName, jcr->errmsg);
322 Dmsg1(420, "get_volume_info(): %s", dir->msg);
323 /* Update dev Volume info in case something changed (e.g. expired) */
324 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
329 * After writing a Volume, create the JobMedia record.
331 bool dir_create_jobmedia_record(DCR *dcr)
334 BSOCK *dir = jcr->dir_bsock;
336 if (!dcr->WroteVol) {
337 return true; /* nothing written to tape */
340 dcr->WroteVol = false;
341 bnet_fsend(dir, Create_job_media, jcr->Job,
342 dcr->VolFirstIndex, dcr->VolLastIndex,
343 dcr->StartFile, dcr->EndFile,
344 dcr->StartBlock, dcr->EndBlock,
345 dcr->Copy, dcr->Stripe);
346 Dmsg1(100, ">dird: %s", dir->msg);
347 if (bnet_recv(dir) <= 0) {
348 Dmsg0(190, "create_jobmedia error bnet_recv\n");
349 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
353 Dmsg1(100, "<dir: %s", dir->msg);
354 if (strcmp(dir->msg, OK_create) != 0) {
355 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
356 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
364 * Update File Attribute data
366 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
369 BSOCK *dir = jcr->dir_bsock;
372 #ifdef NO_ATTRIBUTES_TEST
376 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
377 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
378 sizeof(DEV_RECORD) + rec->data_len);
379 ser_begin(dir->msg + dir->msglen, 0);
380 ser_uint32(rec->VolSessionId);
381 ser_uint32(rec->VolSessionTime);
382 ser_int32(rec->FileIndex);
383 ser_int32(rec->Stream);
384 ser_uint32(rec->data_len);
385 ser_bytes(rec->data, rec->data_len);
386 dir->msglen = ser_length(dir->msg);
387 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
388 return bnet_send(dir);
393 * Request the sysop to create an appendable volume
395 * Entered with device blocked.
396 * Leaves with device blocked.
398 * Returns: true on success (operator issues a mount command)
400 * Note, must create dev->errmsg on error return.
402 * On success, dcr->VolumeName and dcr->VolCatInfo contain
403 * information on suggested volume, but this may not be the
404 * same as what is actually mounted.
406 * When we return with success, the correct tape may or may not
407 * actually be mounted. The calling routine must read it and
410 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
415 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 */
432 OK = dir_find_next_appendable_volume(dcr); /* get suggested volume */
436 unmounted = is_device_unmounted(dev);
438 * If we have a valid volume name and we are not
439 * removable media, return now, or if we have a
440 * Slot for an autochanger, otherwise wait
441 * for the operator to mount the media.
443 if (!unmounted && ((dcr->VolumeName[0] && !dev_cap(dev, CAP_REM) &&
444 dev_cap(dev, CAP_LABEL)) ||
445 (dcr->VolumeName[0] && dcr->VolCatInfo.Slot))) {
446 Dmsg0(400, "Return 1 from mount without wait.\n");
449 jstat = JS_WaitMount;
451 Jmsg(jcr, M_MOUNT, 0, _(
452 "Please mount Volume \"%s\" on Storage Device %s for Job %s\n"
453 "Use \"mount\" command to release Job.\n"),
454 dcr->VolumeName, dev->print_name(), jcr->Job);
455 Dmsg3(400, "Mount %s on %s for Job %s\n",
456 dcr->VolumeName, dcr->dev_name, jcr->Job);
459 jstat = JS_WaitMedia;
461 Jmsg(jcr, M_MOUNT, 0, _(
462 "Job %s waiting. Cannot find any appendable volumes.\n"
463 "Please use the \"label\" command to create a new Volume for:\n"
475 jcr->JobStatus = jstat;
476 dir_send_job_status(jcr);
478 stat = wait_for_sysop(dcr);
480 Dmsg1(400, "Poll timeout in create append vol on device %s\n", dev->print_name());
484 if (stat == ETIMEDOUT) {
485 if (!double_dev_wait_time(dev)) {
486 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
487 dev->print_name(), jcr->Job);
488 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
489 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
490 return false; /* exceeded maximum waits */
494 if (stat == EINVAL) {
496 Mmsg2(dev->errmsg, _("pthread error in mount_next_volume stat=%d ERR=%s\n"),
497 stat, be.strerror(stat));
498 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
503 Jmsg(jcr, M_WARNING, 0, _("pthread error in mount_next_volume stat=%d ERR=%s\n"), stat,
506 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
508 /* If no VolumeName, and cannot get one, try again */
510 if (dcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
511 !dir_find_next_appendable_volume(dcr)) {
513 Jmsg(jcr, M_MOUNT, 0, _(
514 "Someone woke me up, but I cannot find any appendable\n"
515 "volumes for Job=%s.\n"), jcr->Job);
516 /* Restart wait counters after user interaction */
517 init_device_wait_timers(dcr);
521 unmounted = is_device_unmounted(dev);
523 Dmsg0(400, "Device is unmounted. Must wait.\n");
524 continue; /* continue to wait */
528 * Device mounted, we have a volume, break and return
532 set_jcr_job_status(jcr, JS_Running);
533 dir_send_job_status(jcr);
534 Dmsg0(400, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
539 * Request to mount specific Volume
541 * Entered with device blocked and dcr->VolumeName is desired
543 * Leaves with device blocked.
545 * Returns: true on success (operator issues a mount command)
547 * Note, must create dev->errmsg on error return.
550 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
553 DEVICE *dev = dcr->dev;
556 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
557 if (!dcr->VolumeName[0]) {
558 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
561 ASSERT(dev->dev_blocked);
563 if (job_canceled(jcr)) {
564 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
565 jcr->Job, dev->print_name());
573 Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
574 dcr->VolumeName, dev->print_name(), jcr->Job);
575 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
576 dcr->VolumeName, dev->print_name(), jcr->Job);
579 jcr->JobStatus = JS_WaitMount;
580 dir_send_job_status(jcr);
582 stat = wait_for_sysop(dcr); ; /* wait on device */
584 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
585 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
589 if (stat == ETIMEDOUT) {
590 if (!double_dev_wait_time(dev)) {
591 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
592 dev->print_name(), jcr->Job);
593 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
594 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
595 return false; /* exceeded maximum waits */
599 if (stat == EINVAL) {
601 Mmsg2(dev->errmsg, _("pthread error in mount_volume stat=%d ERR=%s\n"),
602 stat, be.strerror(stat));
603 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
608 Jmsg(jcr, M_FATAL, 0, _("pthread error in mount_next_volume stat=%d: ERR=%s\n"), stat,
611 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
614 set_jcr_job_status(jcr, JS_Running);
615 dir_send_job_status(jcr);
616 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");