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.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 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 (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 * Get info on the next appendable volume in the Director's database
217 * Returns: true on success
220 * Volume information returned in dcr
223 bool dir_find_next_appendable_volume(DCR *dcr)
226 BSOCK *dir = jcr->dir_bsock;
229 Dmsg0(200, "dir_find_next_appendable_volume\n");
231 * Try the twenty oldest or most available volumes. Note,
232 * the most available could already be mounted on another
233 * drive, so we continue looking for a not in use Volume.
235 for (int vol_index=1; vol_index < 20; vol_index++) {
236 bash_spaces(dcr->media_type);
237 bash_spaces(dcr->pool_name);
238 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
239 unbash_spaces(dcr->media_type);
240 unbash_spaces(dcr->pool_name);
241 Dmsg1(100, ">dird: %s", dir->msg);
242 if (do_get_volume_info(dcr)) {
243 if (is_volume_in_use(dcr->VolumeName)) {
250 Dmsg0(200, "No volume info, return false\n");
255 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
256 new_volume(dcr->VolumeName, NULL); /* reserve volume */
264 * After writing a Volume, send the updated statistics
265 * back to the director. The information comes from the
268 bool dir_update_volume_info(DCR *dcr, bool label)
271 BSOCK *dir = jcr->dir_bsock;
272 DEVICE *dev = dcr->dev;
273 time_t LastWritten = time(NULL);
274 char ed1[50], ed2[50], ed3[50], ed4[50];
275 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
279 if (vol->VolCatName[0] == 0) {
280 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
281 Pmsg0(000, "NULL Volume name. This shouldn't happen!!!\n");
284 if (dev->can_read()) {
285 Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
286 Pmsg0(000, "Attempt to update_volume_info in read mode!!!\n");
290 Dmsg1(300, "Update cat VolFiles=%d\n", dev->file);
291 /* Just labeled or relabeled the tape */
293 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
294 vol->VolCatBytes = 1; /* indicates tape labeled */
296 pm_strcpy(VolumeName, vol->VolCatName);
297 bash_spaces(VolumeName);
298 InChanger = vol->InChanger;
299 bnet_fsend(dir, Update_media, jcr->Job,
300 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
301 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
302 vol->VolCatMounts, vol->VolCatErrors,
303 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
304 LastWritten, vol->VolCatStatus, vol->Slot, label,
305 InChanger, /* bool in structure */
306 edit_uint64(vol->VolReadTime, ed3),
307 edit_uint64(vol->VolWriteTime, ed4),
309 Dmsg1(100, ">dird: %s", dir->msg);
311 if (!do_get_volume_info(dcr)) {
312 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
313 Pmsg2(000, "Didn't get vol info vol=%s: ERR=%s",
314 vol->VolCatName, jcr->errmsg);
317 Dmsg1(420, "get_volume_info(): %s", dir->msg);
318 /* Update dev Volume info in case something changed (e.g. expired) */
319 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
324 * After writing a Volume, create the JobMedia record.
326 bool dir_create_jobmedia_record(DCR *dcr)
329 BSOCK *dir = jcr->dir_bsock;
331 if (!dcr->WroteVol) {
332 return true; /* nothing written to tape */
335 dcr->WroteVol = false;
336 bnet_fsend(dir, Create_job_media, jcr->Job,
337 dcr->VolFirstIndex, dcr->VolLastIndex,
338 dcr->StartFile, dcr->EndFile,
339 dcr->StartBlock, dcr->EndBlock,
340 dcr->Copy, dcr->Stripe);
341 Dmsg1(100, ">dird: %s", dir->msg);
342 if (bnet_recv(dir) <= 0) {
343 Dmsg0(190, "create_jobmedia error bnet_recv\n");
344 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
348 Dmsg1(100, "<dir: %s", dir->msg);
349 if (strcmp(dir->msg, OK_create) != 0) {
350 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
351 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
359 * Update File Attribute data
361 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
364 BSOCK *dir = jcr->dir_bsock;
367 #ifdef NO_ATTRIBUTES_TEST
371 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
372 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
373 sizeof(DEV_RECORD) + rec->data_len);
374 ser_begin(dir->msg + dir->msglen, 0);
375 ser_uint32(rec->VolSessionId);
376 ser_uint32(rec->VolSessionTime);
377 ser_int32(rec->FileIndex);
378 ser_int32(rec->Stream);
379 ser_uint32(rec->data_len);
380 ser_bytes(rec->data, rec->data_len);
381 dir->msglen = ser_length(dir->msg);
382 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
383 return bnet_send(dir);
388 * Request the sysop to create an appendable volume
390 * Entered with device blocked.
391 * Leaves with device blocked.
393 * Returns: true on success (operator issues a mount command)
395 * Note, must create dev->errmsg on error return.
397 * On success, dcr->VolumeName and dcr->VolCatInfo contain
398 * information on suggested volume, but this may not be the
399 * same as what is actually mounted.
401 * When we return with success, the correct tape may or may not
402 * actually be mounted. The calling routine must read it and
405 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
410 DEVICE *dev = dcr->dev;
413 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
414 ASSERT(dev->dev_blocked);
416 if (job_canceled(jcr)) {
418 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
419 jcr->Job, dev->print_name());
420 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
423 /* First pass, we *know* there are no appendable volumes, so no need to call */
424 if (!first && dir_find_next_appendable_volume(dcr)) { /* get suggested volume */
425 unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
426 (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
428 * If we have a valid volume name and we are not
429 * removable media, return now, or if we have a
430 * Slot for an autochanger, otherwise wait
431 * for the operator to mount the media.
433 if (!unmounted && ((dcr->VolumeName[0] && !dev_cap(dev, CAP_REM) &&
434 dev_cap(dev, CAP_LABEL)) ||
435 (dcr->VolumeName[0] && dcr->VolCatInfo.Slot))) {
436 Dmsg0(400, "Return 1 from mount without wait.\n");
439 jstat = JS_WaitMount;
441 Jmsg(jcr, M_MOUNT, 0, _(
442 "Please mount Volume \"%s\" on Storage Device %s for Job %s\n"
443 "Use \"mount\" command to release Job.\n"),
444 dcr->VolumeName, dev->print_name(), jcr->Job);
445 Dmsg3(400, "Mount %s on %s for Job %s\n",
446 dcr->VolumeName, dcr->dev_name, jcr->Job);
449 jstat = JS_WaitMedia;
451 Jmsg(jcr, M_MOUNT, 0, _(
452 "Job %s waiting. Cannot find any appendable volumes.\n"
453 "Please use the \"label\" command to create a new Volume for:\n"
465 jcr->JobStatus = jstat;
466 dir_send_job_status(jcr);
468 stat = wait_for_sysop(dcr);
470 Dmsg1(400, "Poll timeout in create append vol on device %s\n", dev->print_name());
474 if (stat == ETIMEDOUT) {
475 if (!double_dev_wait_time(dev)) {
476 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
477 dev->print_name(), jcr->Job);
478 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
479 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
480 return false; /* exceeded maximum waits */
484 if (stat == EINVAL) {
486 Mmsg2(dev->errmsg, _("pthread error in mount_next_volume stat=%d ERR=%s\n"),
487 stat, be.strerror(stat));
488 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
493 Jmsg(jcr, M_WARNING, 0, _("pthread error in mount_next_volume stat=%d ERR=%s\n"), stat,
496 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
498 /* If no VolumeName, and cannot get one, try again */
499 if (dcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
500 !dir_find_next_appendable_volume(dcr)) {
501 Jmsg(jcr, M_MOUNT, 0, _(
502 "Someone woke me up, but I cannot find any appendable\n"
503 "volumes for Job=%s.\n"), jcr->Job);
504 /* Restart wait counters after user interaction */
505 init_device_wait_timers(dcr);
508 unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
509 (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
511 continue; /* continue to wait */
515 * Device mounted, we have a volume, break and return
519 set_jcr_job_status(jcr, JS_Running);
520 dir_send_job_status(jcr);
521 Dmsg0(400, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
526 * Request to mount specific Volume
528 * Entered with device blocked and dcr->VolumeName is desired
530 * Leaves with device blocked.
532 * Returns: true on success (operator issues a mount command)
534 * Note, must create dev->errmsg on error return.
537 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
541 DEVICE *dev = dcr->dev;
544 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
545 if (!dcr->VolumeName[0]) {
546 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
549 ASSERT(dev->dev_blocked);
551 if (job_canceled(jcr)) {
552 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
553 jcr->Job, dev->print_name());
558 msg = _("Please mount");
559 Jmsg(jcr, M_MOUNT, 0, _("%s Volume \"%s\" on Storage Device %s for Job %s\n"),
560 msg, dcr->VolumeName, dev->print_name(), jcr->Job);
561 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
562 dcr->VolumeName, dcr->dev_name, jcr->Job);
565 jcr->JobStatus = JS_WaitMount;
566 dir_send_job_status(jcr);
568 stat = wait_for_sysop(dcr); ; /* wait on device */
570 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
571 Dmsg1(400, "Blocked=%s\n", edit_blocked_reason(dev));
575 if (stat == ETIMEDOUT) {
576 if (!double_dev_wait_time(dev)) {
577 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
578 dev->print_name(), jcr->Job);
579 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
580 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
581 return false; /* exceeded maximum waits */
585 if (stat == EINVAL) {
587 Mmsg2(dev->errmsg, _("pthread error in mount_volume stat=%d ERR=%s\n"),
588 stat, be.strerror(stat));
589 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
594 Jmsg(jcr, M_FATAL, 0, _("pthread error in mount_next_volume stat=%d: ERR=%s\n"), stat,
597 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
600 set_jcr_job_status(jcr, JS_Running);
601 dir_send_job_status(jcr);
602 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");