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=%lu"
45 " VolBlocks=%lu VolBytes=%lld VolMounts=%lu VolErrors=%lu VolWrites=%lu"
46 " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
47 " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
48 " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
49 " VolParts=%lu LabelType=%ld";
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(110, "<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(110, "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;
232 Dmsg0(200, "dir_find_next_appendable_volume\n");
234 * Try the twenty oldest or most available volumes. Note,
235 * the most available could already be mounted on another
236 * drive, so we continue looking for a not in use Volume.
239 for (int vol_index=1; vol_index < 20; vol_index++) {
240 bash_spaces(dcr->media_type);
241 bash_spaces(dcr->pool_name);
242 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
243 unbash_spaces(dcr->media_type);
244 unbash_spaces(dcr->pool_name);
245 Dmsg1(100, ">dird: %s", dir->msg);
246 bool ok = do_get_volume_info(dcr);
248 if (dcr->any_volume || !is_volume_in_use(dcr)) {
252 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
256 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
257 dcr->dev->print_name());
263 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
264 new_volume(dcr, dcr->VolumeName); /* reserve volume */
265 unlock_reservations();
268 dcr->VolumeName[0] = 0;
269 unlock_reservations();
275 * After writing a Volume, send the updated statistics
276 * back to the director. The information comes from the
279 bool dir_update_volume_info(DCR *dcr, bool label)
282 BSOCK *dir = jcr->dir_bsock;
283 DEVICE *dev = dcr->dev;
284 time_t LastWritten = time(NULL);
285 char ed1[50], ed2[50], ed3[50], ed4[50];
286 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
290 if (vol->VolCatName[0] == 0) {
291 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
292 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
295 if (dev->can_read()) {
296 Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
297 Pmsg0(000, _("Attempt to update_volume_info in read mode!!!\n"));
301 Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
302 /* Just labeled or relabeled the tape */
304 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
306 pm_strcpy(VolumeName, vol->VolCatName);
307 bash_spaces(VolumeName);
308 InChanger = vol->InChanger;
309 bnet_fsend(dir, Update_media, jcr->Job,
310 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
311 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
312 vol->VolCatMounts, vol->VolCatErrors,
313 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
314 LastWritten, vol->VolCatStatus, vol->Slot, label,
315 InChanger, /* bool in structure */
316 edit_uint64(vol->VolReadTime, ed3),
317 edit_uint64(vol->VolWriteTime, ed4),
319 Dmsg1(100, ">dird: %s", dir->msg);
321 /* Do not lock device here because it may be locked from label */
322 if (!do_get_volume_info(dcr)) {
323 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
324 Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"),
325 vol->VolCatName, jcr->errmsg);
328 Dmsg1(420, "get_volume_info(): %s", dir->msg);
329 /* Update dev Volume info in case something changed (e.g. expired) */
330 memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
335 * After writing a Volume, create the JobMedia record.
337 bool dir_create_jobmedia_record(DCR *dcr)
340 BSOCK *dir = jcr->dir_bsock;
342 if (!dcr->WroteVol) {
343 return true; /* nothing written to tape */
346 dcr->WroteVol = false;
347 bnet_fsend(dir, Create_job_media, jcr->Job,
348 dcr->VolFirstIndex, dcr->VolLastIndex,
349 dcr->StartFile, dcr->EndFile,
350 dcr->StartBlock, dcr->EndBlock,
351 dcr->Copy, dcr->Stripe);
352 Dmsg1(100, ">dird: %s", dir->msg);
353 if (bnet_recv(dir) <= 0) {
354 Dmsg0(190, "create_jobmedia error bnet_recv\n");
355 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
359 Dmsg1(100, "<dir: %s", dir->msg);
360 if (strcmp(dir->msg, OK_create) != 0) {
361 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
362 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
370 * Update File Attribute data
372 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
375 BSOCK *dir = jcr->dir_bsock;
378 #ifdef NO_ATTRIBUTES_TEST
382 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
383 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
384 sizeof(DEV_RECORD) + rec->data_len);
385 ser_begin(dir->msg + dir->msglen, 0);
386 ser_uint32(rec->VolSessionId);
387 ser_uint32(rec->VolSessionTime);
388 ser_int32(rec->FileIndex);
389 ser_int32(rec->Stream);
390 ser_uint32(rec->data_len);
391 ser_bytes(rec->data, rec->data_len);
392 dir->msglen = ser_length(dir->msg);
393 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
394 return bnet_send(dir);
399 * Request the sysop to create an appendable volume
401 * Entered with device blocked.
402 * Leaves with device blocked.
404 * Returns: true on success (operator issues a mount command)
406 * Note, must create dev->errmsg on error return.
408 * On success, dcr->VolumeName and dcr->VolCatInfo contain
409 * information on suggested volume, but this may not be the
410 * same as what is actually mounted.
412 * When we return with success, the correct tape may or may not
413 * actually be mounted. The calling routine must read it and
416 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
418 int stat = W_TIMEOUT;
419 DEVICE *dev = dcr->dev;
421 bool got_vol = false;
423 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
424 ASSERT(dev->dev_blocked);
426 if (job_canceled(jcr)) {
428 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
429 jcr->Job, dev->print_name());
430 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
434 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
439 if (stat == W_TIMEOUT || stat == W_MOUNT) {
440 Jmsg(jcr, M_MOUNT, 0, _(
441 "Job %s waiting. Cannot find any appendable volumes.\n"
442 "Please use the \"label\" command to create a new Volume for:\n"
453 set_jcr_job_status(jcr, JS_WaitMedia);
454 dir_send_job_status(jcr);
456 stat = wait_for_sysop(dcr);
457 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
459 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
463 if (stat == W_TIMEOUT) {
464 if (!double_dev_wait_time(dev)) {
465 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
466 dev->print_name(), jcr->Job);
467 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
468 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
469 return false; /* exceeded maximum waits */
473 if (stat == W_ERROR) {
475 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
476 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
479 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
481 set_jcr_job_status(jcr, JS_Running);
482 dir_send_job_status(jcr);
483 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
488 * Request to mount specific Volume
490 * Entered with device blocked and dcr->VolumeName is desired
492 * Leaves with device blocked.
494 * Returns: true on success (operator issues a mount command)
496 * Note, must create dev->errmsg on error return.
499 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
501 int stat = W_TIMEOUT;
502 DEVICE *dev = dcr->dev;
505 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
506 if (!dcr->VolumeName[0]) {
507 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
510 ASSERT(dev->dev_blocked);
512 if (job_canceled(jcr)) {
513 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
514 jcr->Job, dev->print_name());
523 * If we are not polling, and the wait timeout or the
524 * user explicitly did a mount, send him the message.
527 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
528 Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
529 dcr->VolumeName, dev->print_name(), jcr->Job);
530 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
531 dcr->VolumeName, dev->print_name(), jcr->Job);
534 set_jcr_job_status(jcr, JS_WaitMount);
535 dir_send_job_status(jcr);
537 stat = wait_for_sysop(dcr); /* wait on device */
538 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
540 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
541 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
545 if (stat == W_TIMEOUT) {
546 if (!double_dev_wait_time(dev)) {
547 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
548 dev->print_name(), jcr->Job);
549 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
550 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
551 return false; /* exceeded maximum waits */
555 if (stat == W_ERROR) {
557 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
558 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
561 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
564 set_jcr_job_status(jcr, JS_Running);
565 dir_send_job_status(jcr);
566 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");