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-2006 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"
34 " VolFirstWritten=%s VolParts=%u\n";
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 MediaId=%s\n";
38 static char FileAttributes[] = "UpdCat Job=%s FileAttributes ";
39 static char Job_status[] = "Status Job=%s JobStatus=%d\n";
41 /* Responses received from the Director */
42 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
43 " VolBlocks=%lu VolBytes=%lld VolMounts=%lu VolErrors=%lu VolWrites=%lu"
44 " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
45 " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
46 " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
47 " VolParts=%lu LabelType=%ld MediaId=%lld\n";
50 static char OK_create[] = "1000 OK CreateJobMedia\n";
54 static char Device_update[] = "DevUpd Job=%s device=%s "
55 "append=%d read=%d num_writers=%d "
56 "open=%d labeled=%d offline=%d "
57 "reserved=%d max_writers=%d "
58 "autoselect=%d autochanger=%d "
59 "changer_name=%s media_type=%s volume_name=%s\n";
62 /* Send update information about a device to Director */
63 bool dir_update_device(JCR *jcr, DEVICE *dev)
65 BSOCK *dir = jcr->dir_bsock;
66 POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
67 DEVRES *device = dev->device;
70 pm_strcpy(dev_name, device->hdr.name);
71 bash_spaces(dev_name);
72 if (dev->is_labeled()) {
73 pm_strcpy(VolumeName, dev->VolHdr.VolumeName);
75 pm_strcpy(VolumeName, "*");
77 bash_spaces(VolumeName);
78 pm_strcpy(MediaType, device->media_type);
79 bash_spaces(MediaType);
80 if (device->changer_res) {
81 pm_strcpy(ChangerName, device->changer_res->hdr.name);
82 bash_spaces(ChangerName);
84 pm_strcpy(ChangerName, "*");
86 ok =bnet_fsend(dir, Device_update,
90 dev->can_read()!=0, dev->num_writers,
91 dev->is_open()!=0, dev->is_labeled()!=0,
92 dev->is_offline()!=0, dev->reserved_device,
93 dev->is_tape()?100000:1,
95 ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
96 Dmsg1(100, ">dird: %s\n", dir->msg);
100 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
102 BSOCK *dir = jcr->dir_bsock;
103 POOL_MEM dev_name, MediaType;
107 pm_strcpy(dev_name, changer->hdr.name);
108 bash_spaces(dev_name);
109 device = (DEVRES *)changer->device->first();
110 pm_strcpy(MediaType, device->media_type);
111 bash_spaces(MediaType);
112 /* This is mostly to indicate that we are here */
113 ok = bnet_fsend(dir, Device_update,
115 dev_name.c_str(), /* Changer name */
116 0, 0, 0, /* append, read, num_writers */
117 0, 0, 0, /* is_open, is_labeled, offline */
118 0, 0, /* reserved, max_writers */
120 changer->device->size(), /* Number of devices */
122 "*", /* ChangerName */
123 MediaType.c_str(), /* MediaType */
125 Dmsg1(100, ">dird: %s\n", dir->msg);
132 * Send current JobStatus to Director
134 bool dir_send_job_status(JCR *jcr)
136 return bnet_fsend(jcr->dir_bsock, Job_status, jcr->Job, jcr->JobStatus);
140 * Common routine for:
141 * dir_get_volume_info()
143 * dir_find_next_appendable_volume()
145 * Returns: true on success and vol info in dcr->VolCatInfo
148 static bool do_get_volume_info(DCR *dcr)
151 BSOCK *dir = jcr->dir_bsock;
156 dcr->VolumeName[0] = 0; /* No volume */
157 if (bnet_recv(dir) <= 0) {
158 Dmsg0(200, "getvolname error bnet_recv\n");
159 Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
162 memset(&vol, 0, sizeof(vol));
163 Dmsg1(100, "<dird %s", dir->msg);
164 n = sscanf(dir->msg, OK_media, vol.VolCatName,
165 &vol.VolCatJobs, &vol.VolCatFiles,
166 &vol.VolCatBlocks, &vol.VolCatBytes,
167 &vol.VolCatMounts, &vol.VolCatErrors,
168 &vol.VolCatWrites, &vol.VolCatMaxBytes,
169 &vol.VolCatCapacityBytes, vol.VolCatStatus,
170 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
171 &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
172 &vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
173 &vol.LabelType, &vol.VolMediaId);
175 Dmsg2(100, "Bad response from Dir fields=%d: %s", n, dir->msg);
176 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
179 vol.InChanger = InChanger; /* bool in structure */
180 unbash_spaces(vol.VolCatName);
181 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
182 dcr->VolCatInfo = vol; /* structure assignment */
184 Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n",
185 vol.Slot, vol.VolCatName);
191 * Get Volume info for a specific volume from the Director's Database
193 * Returns: true on success (Director guarantees that Pool and MediaType
194 * are correct and VolStatus==Append or
195 * VolStatus==Recycle)
198 * Volume information returned in dcr->VolCatInfo
200 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
203 BSOCK *dir = jcr->dir_bsock;
205 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
206 bash_spaces(dcr->VolCatInfo.VolCatName);
207 bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
208 writing==GET_VOL_INFO_FOR_WRITE?1:0);
209 Dmsg1(100, ">dird: %s", dir->msg);
210 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.
237 for (int vol_index=1; vol_index < 20; vol_index++) {
238 bash_spaces(dcr->media_type);
239 bash_spaces(dcr->pool_name);
240 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
241 unbash_spaces(dcr->media_type);
242 unbash_spaces(dcr->pool_name);
243 Dmsg1(100, ">dird: %s", dir->msg);
244 bool ok = do_get_volume_info(dcr);
246 if (dcr->any_volume || !is_volume_in_use(dcr)) {
250 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
254 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
255 dcr->dev->print_name());
261 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
262 new_volume(dcr, dcr->VolumeName); /* reserve volume */
263 unlock_reservations();
266 dcr->VolumeName[0] = 0;
267 unlock_reservations();
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 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
284 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
288 /* If system job, do not update catalog */
289 if (jcr->JobType == JT_SYSTEM) {
293 if (vol->VolCatName[0] == 0) {
294 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
295 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
298 if (dev->can_read()) {
299 Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
300 Pmsg0(000, _("Attempt to update_volume_info in read mode!!!\n"));
304 Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
305 /* Just labeled or relabeled the tape */
307 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
309 pm_strcpy(VolumeName, vol->VolCatName);
310 bash_spaces(VolumeName);
311 InChanger = vol->InChanger;
312 bnet_fsend(dir, Update_media, jcr->Job,
313 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
314 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
315 vol->VolCatMounts, vol->VolCatErrors,
316 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
317 LastWritten, vol->VolCatStatus, vol->Slot, label,
318 InChanger, /* bool in structure */
319 edit_uint64(vol->VolReadTime, ed3),
320 edit_uint64(vol->VolWriteTime, ed4),
321 edit_uint64(vol->VolFirstWritten, ed5),
323 Dmsg1(100, ">dird: %s", dir->msg);
325 /* Do not lock device here because it may be locked from label */
326 if (!do_get_volume_info(dcr)) {
327 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
328 Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"),
329 vol->VolCatName, jcr->errmsg);
332 Dmsg1(420, "get_volume_info(): %s", dir->msg);
333 /* Update dev Volume info in case something changed (e.g. expired) */
334 dev->VolCatInfo = dcr->VolCatInfo;
339 * After writing a Volume, create the JobMedia record.
341 bool dir_create_jobmedia_record(DCR *dcr)
344 BSOCK *dir = jcr->dir_bsock;
347 /* If system job, do not update catalog */
348 if (jcr->JobType == JT_SYSTEM) {
352 if (!dcr->WroteVol) {
353 return true; /* nothing written to tape */
356 dcr->WroteVol = false;
357 bnet_fsend(dir, Create_job_media, jcr->Job,
358 dcr->VolFirstIndex, dcr->VolLastIndex,
359 dcr->StartFile, dcr->EndFile,
360 dcr->StartBlock, dcr->EndBlock,
361 dcr->Copy, dcr->Stripe,
362 edit_uint64(dcr->dev->VolCatInfo.VolMediaId, ed1));
363 Dmsg1(100, ">dird: %s", dir->msg);
364 if (bnet_recv(dir) <= 0) {
365 Dmsg0(190, "create_jobmedia error bnet_recv\n");
366 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
370 Dmsg1(100, "<dir: %s", dir->msg);
371 if (strcmp(dir->msg, OK_create) != 0) {
372 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
373 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
381 * Update File Attribute data
383 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
386 BSOCK *dir = jcr->dir_bsock;
389 #ifdef NO_ATTRIBUTES_TEST
393 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
394 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
395 sizeof(DEV_RECORD) + rec->data_len);
396 ser_begin(dir->msg + dir->msglen, 0);
397 ser_uint32(rec->VolSessionId);
398 ser_uint32(rec->VolSessionTime);
399 ser_int32(rec->FileIndex);
400 ser_int32(rec->Stream);
401 ser_uint32(rec->data_len);
402 ser_bytes(rec->data, rec->data_len);
403 dir->msglen = ser_length(dir->msg);
404 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
405 return bnet_send(dir);
410 * Request the sysop to create an appendable volume
412 * Entered with device blocked.
413 * Leaves with device blocked.
415 * Returns: true on success (operator issues a mount command)
417 * Note, must create dev->errmsg on error return.
419 * On success, dcr->VolumeName and dcr->VolCatInfo contain
420 * information on suggested volume, but this may not be the
421 * same as what is actually mounted.
423 * When we return with success, the correct tape may or may not
424 * actually be mounted. The calling routine must read it and
427 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
429 int stat = W_TIMEOUT;
430 DEVICE *dev = dcr->dev;
432 bool got_vol = false;
434 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
435 ASSERT(dev->dev_blocked);
437 if (job_canceled(jcr)) {
439 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
440 jcr->Job, dev->print_name());
441 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
445 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
450 if (stat == W_TIMEOUT || stat == W_MOUNT) {
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"
464 set_jcr_job_status(jcr, JS_WaitMedia);
465 dir_send_job_status(jcr);
467 stat = wait_for_sysop(dcr);
468 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
470 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
474 if (stat == W_TIMEOUT) {
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(100, "Gave up waiting on device %s\n", dev->print_name());
480 return false; /* exceeded maximum waits */
484 if (stat == W_ERROR) {
486 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
487 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
490 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
492 set_jcr_job_status(jcr, JS_Running);
493 dir_send_job_status(jcr);
494 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
499 * Request to mount specific Volume
501 * Entered with device blocked and dcr->VolumeName is desired
503 * Leaves with device blocked.
505 * Returns: true on success (operator issues a mount command)
507 * Note, must create dev->errmsg on error return.
510 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
512 int stat = W_TIMEOUT;
513 DEVICE *dev = dcr->dev;
516 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
517 if (!dcr->VolumeName[0]) {
518 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
521 ASSERT(dev->dev_blocked);
523 if (job_canceled(jcr)) {
524 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
525 jcr->Job, dev->print_name());
534 * If we are not polling, and the wait timeout or the
535 * user explicitly did a mount, send him the message.
538 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
539 Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
540 dcr->VolumeName, dev->print_name(), jcr->Job);
541 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
542 dcr->VolumeName, dev->print_name(), jcr->Job);
545 set_jcr_job_status(jcr, JS_WaitMount);
546 dir_send_job_status(jcr);
548 stat = wait_for_sysop(dcr); /* wait on device */
549 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
551 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
552 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
556 if (stat == W_TIMEOUT) {
557 if (!double_dev_wait_time(dev)) {
558 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
559 dev->print_name(), jcr->Job);
560 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
561 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
562 return false; /* exceeded maximum waits */
566 if (stat == W_ERROR) {
568 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
569 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
572 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
575 set_jcr_job_status(jcr, JS_Running);
576 dir_send_job_status(jcr);
577 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");