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"
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, dir->msg);
178 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), 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);
219 * Get info on the next appendable volume in the Director's database
220 * Returns: true on success
223 * Volume information returned in dcr
226 bool dir_find_next_appendable_volume(DCR *dcr)
229 BSOCK *dir = jcr->dir_bsock;
231 /* This mutex should keep different devices from getting the
234 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
236 Dmsg0(200, "dir_find_next_appendable_volume\n");
238 * Try the twenty oldest or most available volumes. Note,
239 * the most available could already be mounted on another
240 * 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 (dcr->any_volume || !is_volume_in_use(dcr)) {
256 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
260 Dmsg0(200, "No volume info, return false\n");
266 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
267 new_volume(dcr, dcr->VolumeName); /* reserve volume */
271 dcr->VolumeName[0] = 0;
278 * After writing a Volume, send the updated statistics
279 * back to the director. The information comes from the
282 bool dir_update_volume_info(DCR *dcr, bool label)
285 BSOCK *dir = jcr->dir_bsock;
286 DEVICE *dev = dcr->dev;
287 time_t LastWritten = time(NULL);
288 char ed1[50], ed2[50], ed3[50], ed4[50];
289 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
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));
308 vol->VolCatBytes = 1; /* indicates tape labeled */
310 pm_strcpy(VolumeName, vol->VolCatName);
311 bash_spaces(VolumeName);
312 InChanger = vol->InChanger;
313 bnet_fsend(dir, Update_media, jcr->Job,
314 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
315 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
316 vol->VolCatMounts, vol->VolCatErrors,
317 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
318 LastWritten, vol->VolCatStatus, vol->Slot, label,
319 InChanger, /* bool in structure */
320 edit_uint64(vol->VolReadTime, ed3),
321 edit_uint64(vol->VolWriteTime, ed4),
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 Pmsg2(000, _("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 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
339 * After writing a Volume, create the JobMedia record.
341 bool dir_create_jobmedia_record(DCR *dcr)
344 BSOCK *dir = jcr->dir_bsock;
346 if (!dcr->WroteVol) {
347 return true; /* nothing written to tape */
350 dcr->WroteVol = false;
351 bnet_fsend(dir, Create_job_media, jcr->Job,
352 dcr->VolFirstIndex, dcr->VolLastIndex,
353 dcr->StartFile, dcr->EndFile,
354 dcr->StartBlock, dcr->EndBlock,
355 dcr->Copy, dcr->Stripe);
356 Dmsg1(100, ">dird: %s", dir->msg);
357 if (bnet_recv(dir) <= 0) {
358 Dmsg0(190, "create_jobmedia error bnet_recv\n");
359 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
363 Dmsg1(100, "<dir: %s", dir->msg);
364 if (strcmp(dir->msg, OK_create) != 0) {
365 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
366 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
374 * Update File Attribute data
376 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
379 BSOCK *dir = jcr->dir_bsock;
382 #ifdef NO_ATTRIBUTES_TEST
386 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
387 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
388 sizeof(DEV_RECORD) + rec->data_len);
389 ser_begin(dir->msg + dir->msglen, 0);
390 ser_uint32(rec->VolSessionId);
391 ser_uint32(rec->VolSessionTime);
392 ser_int32(rec->FileIndex);
393 ser_int32(rec->Stream);
394 ser_uint32(rec->data_len);
395 ser_bytes(rec->data, rec->data_len);
396 dir->msglen = ser_length(dir->msg);
397 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
398 return bnet_send(dir);
403 * Request the sysop to create an appendable volume
405 * Entered with device blocked.
406 * Leaves with device blocked.
408 * Returns: true on success (operator issues a mount command)
410 * Note, must create dev->errmsg on error return.
412 * On success, dcr->VolumeName and dcr->VolCatInfo contain
413 * information on suggested volume, but this may not be the
414 * same as what is actually mounted.
416 * When we return with success, the correct tape may or may not
417 * actually be mounted. The calling routine must read it and
420 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
422 int stat = W_TIMEOUT;
423 DEVICE *dev = dcr->dev;
425 bool got_vol = false;
427 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
428 ASSERT(dev->dev_blocked);
430 if (job_canceled(jcr)) {
432 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
433 jcr->Job, dev->print_name());
434 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
438 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
443 if (stat == W_TIMEOUT || stat == W_MOUNT) {
444 Jmsg(jcr, M_MOUNT, 0, _(
445 "Job %s waiting. Cannot find any appendable volumes.\n"
446 "Please use the \"label\" command to create a new Volume for:\n"
457 jcr->JobStatus = JS_WaitMedia;
458 dir_send_job_status(jcr);
460 stat = wait_for_sysop(dcr);
461 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
463 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
467 if (stat == W_TIMEOUT) {
468 if (!double_dev_wait_time(dev)) {
469 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
470 dev->print_name(), jcr->Job);
471 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
472 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
473 return false; /* exceeded maximum waits */
477 if (stat == W_ERROR) {
479 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
480 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
483 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
485 set_jcr_job_status(jcr, JS_Running);
486 dir_send_job_status(jcr);
487 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
492 * Request to mount specific Volume
494 * Entered with device blocked and dcr->VolumeName is desired
496 * Leaves with device blocked.
498 * Returns: true on success (operator issues a mount command)
500 * Note, must create dev->errmsg on error return.
503 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
505 int stat = W_TIMEOUT;
506 DEVICE *dev = dcr->dev;
509 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
510 if (!dcr->VolumeName[0]) {
511 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
514 ASSERT(dev->dev_blocked);
516 if (job_canceled(jcr)) {
517 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
518 jcr->Job, dev->print_name());
527 * If we are not polling, and the wait timeout or the
528 * user explicitly did a mount, send him the message.
531 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
532 Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
533 dcr->VolumeName, dev->print_name(), jcr->Job);
534 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
535 dcr->VolumeName, dev->print_name(), jcr->Job);
538 jcr->JobStatus = JS_WaitMount;
539 dir_send_job_status(jcr);
541 stat = wait_for_sysop(dcr); /* wait on device */
542 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
544 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
545 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
549 if (stat == W_TIMEOUT) {
550 if (!double_dev_wait_time(dev)) {
551 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
552 dev->print_name(), jcr->Job);
553 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
554 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
555 return false; /* exceeded maximum waits */
559 if (stat == W_ERROR) {
561 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
562 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
565 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
568 set_jcr_job_status(jcr, JS_Running);
569 dir_send_job_status(jcr);
570 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");