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 return true 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 bool OK = do_get_volume_info(dcr);
222 * Get info on the next appendable volume in the Director's database
223 * Returns: true on success
226 * Volume information returned in dcr
229 bool dir_find_next_appendable_volume(DCR *dcr)
232 BSOCK *dir = jcr->dir_bsock;
235 Dmsg0(200, "dir_find_next_appendable_volume\n");
237 * Try the twenty oldest or most available volumes. Note,
238 * the most available could already be mounted on another
239 * drive, so we continue looking for a not in use Volume.
241 for (int vol_index=1; vol_index < 20; vol_index++) {
242 bash_spaces(dcr->media_type);
243 bash_spaces(dcr->pool_name);
244 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
245 unbash_spaces(dcr->media_type);
246 unbash_spaces(dcr->pool_name);
247 Dmsg1(100, ">dird: %s", dir->msg);
248 bool OK = do_get_volume_info(dcr);
250 if (is_volume_in_use(dcr)) {
251 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
258 Dmsg0(200, "No volume info, return false\n");
263 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
264 new_volume(dcr, dcr->VolumeName); /* reserve volume */
267 dcr->VolumeName[0] = 0;
273 * After writing a Volume, send the updated statistics
274 * back to the director. The information comes from the
277 bool dir_update_volume_info(DCR *dcr, bool label)
280 BSOCK *dir = jcr->dir_bsock;
281 DEVICE *dev = dcr->dev;
282 time_t LastWritten = time(NULL);
283 char ed1[50], ed2[50], ed3[50], ed4[50];
284 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
288 if (vol->VolCatName[0] == 0) {
289 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
290 Pmsg0(000, "NULL Volume name. This shouldn't happen!!!\n");
293 if (dev->can_read()) {
294 Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
295 Pmsg0(000, "Attempt to update_volume_info in read mode!!!\n");
299 Dmsg1(300, "Update cat VolFiles=%d\n", dev->file);
300 /* Just labeled or relabeled the tape */
302 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
303 vol->VolCatBytes = 1; /* indicates tape labeled */
305 pm_strcpy(VolumeName, vol->VolCatName);
306 bash_spaces(VolumeName);
307 InChanger = vol->InChanger;
308 bnet_fsend(dir, Update_media, jcr->Job,
309 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
310 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
311 vol->VolCatMounts, vol->VolCatErrors,
312 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
313 LastWritten, vol->VolCatStatus, vol->Slot, label,
314 InChanger, /* bool in structure */
315 edit_uint64(vol->VolReadTime, ed3),
316 edit_uint64(vol->VolWriteTime, ed4),
318 Dmsg1(100, ">dird: %s", dir->msg);
320 /* Do not lock device here because it may be locked from label */
321 if (!do_get_volume_info(dcr)) {
322 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
323 Pmsg2(000, "Didn't get vol info vol=%s: ERR=%s",
324 vol->VolCatName, jcr->errmsg);
327 Dmsg1(420, "get_volume_info(): %s", dir->msg);
328 /* Update dev Volume info in case something changed (e.g. expired) */
329 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
334 * After writing a Volume, create the JobMedia record.
336 bool dir_create_jobmedia_record(DCR *dcr)
339 BSOCK *dir = jcr->dir_bsock;
341 if (!dcr->WroteVol) {
342 return true; /* nothing written to tape */
345 dcr->WroteVol = false;
346 bnet_fsend(dir, Create_job_media, jcr->Job,
347 dcr->VolFirstIndex, dcr->VolLastIndex,
348 dcr->StartFile, dcr->EndFile,
349 dcr->StartBlock, dcr->EndBlock,
350 dcr->Copy, dcr->Stripe);
351 Dmsg1(100, ">dird: %s", dir->msg);
352 if (bnet_recv(dir) <= 0) {
353 Dmsg0(190, "create_jobmedia error bnet_recv\n");
354 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
358 Dmsg1(100, "<dir: %s", dir->msg);
359 if (strcmp(dir->msg, OK_create) != 0) {
360 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
361 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
369 * Update File Attribute data
371 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
374 BSOCK *dir = jcr->dir_bsock;
377 #ifdef NO_ATTRIBUTES_TEST
381 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
382 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
383 sizeof(DEV_RECORD) + rec->data_len);
384 ser_begin(dir->msg + dir->msglen, 0);
385 ser_uint32(rec->VolSessionId);
386 ser_uint32(rec->VolSessionTime);
387 ser_int32(rec->FileIndex);
388 ser_int32(rec->Stream);
389 ser_uint32(rec->data_len);
390 ser_bytes(rec->data, rec->data_len);
391 dir->msglen = ser_length(dir->msg);
392 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
393 return bnet_send(dir);
398 * Request the sysop to create an appendable volume
400 * Entered with device blocked.
401 * Leaves with device blocked.
403 * Returns: true on success (operator issues a mount command)
405 * Note, must create dev->errmsg on error return.
407 * On success, dcr->VolumeName and dcr->VolCatInfo contain
408 * information on suggested volume, but this may not be the
409 * same as what is actually mounted.
411 * When we return with success, the correct tape may or may not
412 * actually be mounted. The calling routine must read it and
415 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
420 DEVICE *dev = dcr->dev;
424 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
425 ASSERT(dev->dev_blocked);
427 if (job_canceled(jcr)) {
429 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
430 jcr->Job, dev->print_name());
431 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
434 /* First pass, we *know* there are no appendable volumes, so no need to call */
437 OK = dir_find_next_appendable_volume(dcr); /* get suggested volume */
441 unmounted = is_device_unmounted(dev);
443 * If we have a valid volume name and we are not
444 * removable media, return now, or if we have a
445 * Slot for an autochanger, otherwise wait
446 * for the operator to mount the media.
448 if (!unmounted && ((dcr->VolumeName[0] && !dev_cap(dev, CAP_REM) &&
449 dev_cap(dev, CAP_LABEL)) ||
450 (dcr->VolumeName[0] && dcr->VolCatInfo.Slot))) {
451 Dmsg0(400, "Return 1 from mount without wait.\n");
454 jstat = JS_WaitMount;
456 Jmsg(jcr, M_MOUNT, 0, _(
457 "Please mount Volume \"%s\" on Storage Device %s for Job %s\n"
458 "Use \"mount\" command to release Job.\n"),
459 dcr->VolumeName, dev->print_name(), jcr->Job);
460 Dmsg3(400, "Mount %s on %s for Job %s\n",
461 dcr->VolumeName, dcr->dev_name, jcr->Job);
464 jstat = JS_WaitMedia;
466 Jmsg(jcr, M_MOUNT, 0, _(
467 "Job %s waiting. Cannot find any appendable volumes.\n"
468 "Please use the \"label\" command to create a new Volume for:\n"
480 jcr->JobStatus = jstat;
481 dir_send_job_status(jcr);
483 stat = wait_for_sysop(dcr);
485 Dmsg1(400, "Poll timeout in create append vol on device %s\n", dev->print_name());
489 if (stat == ETIMEDOUT) {
490 if (!double_dev_wait_time(dev)) {
491 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
492 dev->print_name(), jcr->Job);
493 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
494 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
495 return false; /* exceeded maximum waits */
499 if (stat == EINVAL) {
501 Mmsg2(dev->errmsg, _("pthread error in mount_next_volume stat=%d ERR=%s\n"),
502 stat, be.strerror(stat));
503 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
508 Jmsg(jcr, M_WARNING, 0, _("pthread error in mount_next_volume stat=%d ERR=%s\n"), stat,
511 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
513 /* If no VolumeName, and cannot get one, try again */
515 if (dcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
516 !dir_find_next_appendable_volume(dcr)) {
518 Jmsg(jcr, M_MOUNT, 0, _(
519 "Someone woke me up, but I cannot find any appendable\n"
520 "volumes for Job=%s.\n"), jcr->Job);
521 /* Restart wait counters after user interaction */
522 init_device_wait_timers(dcr);
526 unmounted = is_device_unmounted(dev);
528 Dmsg0(400, "Device is unmounted. Must wait.\n");
529 continue; /* continue to wait */
533 * Device mounted, we have a volume, break and return
537 set_jcr_job_status(jcr, JS_Running);
538 dir_send_job_status(jcr);
539 Dmsg0(400, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
544 * Request to mount specific Volume
546 * Entered with device blocked and dcr->VolumeName is desired
548 * Leaves with device blocked.
550 * Returns: true on success (operator issues a mount command)
552 * Note, must create dev->errmsg on error return.
555 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
559 DEVICE *dev = dcr->dev;
562 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
563 if (!dcr->VolumeName[0]) {
564 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
567 ASSERT(dev->dev_blocked);
569 if (job_canceled(jcr)) {
570 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
571 jcr->Job, dev->print_name());
576 msg = _("Please mount");
577 Jmsg(jcr, M_MOUNT, 0, _("%s Volume \"%s\" on Storage Device %s for Job %s\n"),
578 msg, dcr->VolumeName, dev->print_name(), jcr->Job);
579 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
580 dcr->VolumeName, dev->print_name(), jcr->Job);
583 jcr->JobStatus = JS_WaitMount;
584 dir_send_job_status(jcr);
586 stat = wait_for_sysop(dcr); ; /* wait on device */
588 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
589 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
593 if (stat == ETIMEDOUT) {
594 if (!double_dev_wait_time(dev)) {
595 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
596 dev->print_name(), jcr->Job);
597 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
598 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
599 return false; /* exceeded maximum waits */
603 if (stat == EINVAL) {
605 Mmsg2(dev->errmsg, _("pthread error in mount_volume stat=%d ERR=%s\n"),
606 stat, be.strerror(stat));
607 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
612 Jmsg(jcr, M_FATAL, 0, _("pthread error in mount_next_volume stat=%d: ERR=%s\n"), stat,
615 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
618 set_jcr_job_status(jcr, JS_Running);
619 dir_send_job_status(jcr);
620 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");