2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Subroutines to handle Catalog reqests sent to the Director
30 * Reqests/commands from the Director are handled in dircmd.c
32 * Kern Sibbald, December 2000
37 #include "bacula.h" /* pull in global headers */
38 #include "stored.h" /* pull in Storage Deamon headers */
40 /* Requests sent to the Director */
41 static char Find_media[] = "CatReq Job=%s FindMedia=%d pool_name=%s media_type=%s\n";
42 static char Get_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s write=%d\n";
43 static char Update_media[] = "CatReq Job=%s UpdateMedia VolName=%s"
44 " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%s VolMounts=%u"
45 " VolErrors=%u VolWrites=%u MaxVolBytes=%s EndTime=%d VolStatus=%s"
46 " Slot=%d relabel=%d InChanger=%d VolReadTime=%s VolWriteTime=%s"
47 " VolFirstWritten=%s VolParts=%u\n";
48 static char Create_job_media[] = "CatReq Job=%s CreateJobMedia"
49 " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u"
50 " StartBlock=%u EndBlock=%u Copy=%d Strip=%d MediaId=%s\n";
51 static char FileAttributes[] = "UpdCat Job=%s FileAttributes ";
52 static char Job_status[] = "Status Job=%s JobStatus=%d\n";
54 /* Responses received from the Director */
55 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
56 " VolBlocks=%lu VolBytes=%lld VolMounts=%lu VolErrors=%lu VolWrites=%lu"
57 " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
58 " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
59 " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
60 " VolParts=%lu LabelType=%ld MediaId=%lld\n";
63 static char OK_create[] = "1000 OK CreateJobMedia\n";
65 static pthread_mutex_t vol_info_mutex = PTHREAD_MUTEX_INITIALIZER;
69 static char Device_update[] = "DevUpd Job=%s device=%s "
70 "append=%d read=%d num_writers=%d "
71 "open=%d labeled=%d offline=%d "
72 "reserved=%d max_writers=%d "
73 "autoselect=%d autochanger=%d "
74 "changer_name=%s media_type=%s volume_name=%s\n";
77 /* Send update information about a device to Director */
78 bool dir_update_device(JCR *jcr, DEVICE *dev)
80 BSOCK *dir = jcr->dir_bsock;
81 POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
82 DEVRES *device = dev->device;
85 pm_strcpy(dev_name, device->hdr.name);
86 bash_spaces(dev_name);
87 if (dev->is_labeled()) {
88 pm_strcpy(VolumeName, dev->VolHdr.VolumeName);
90 pm_strcpy(VolumeName, "*");
92 bash_spaces(VolumeName);
93 pm_strcpy(MediaType, device->media_type);
94 bash_spaces(MediaType);
95 if (device->changer_res) {
96 pm_strcpy(ChangerName, device->changer_res->hdr.name);
97 bash_spaces(ChangerName);
99 pm_strcpy(ChangerName, "*");
101 ok =bnet_fsend(dir, Device_update,
104 dev->can_append()!=0,
105 dev->can_read()!=0, dev->num_writers,
106 dev->is_open()!=0, dev->is_labeled()!=0,
107 dev->is_offline()!=0, dev->reserved_device,
108 dev->is_tape()?100000:1,
110 ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
111 Dmsg1(100, ">dird: %s\n", dir->msg);
115 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
117 BSOCK *dir = jcr->dir_bsock;
118 POOL_MEM dev_name, MediaType;
122 pm_strcpy(dev_name, changer->hdr.name);
123 bash_spaces(dev_name);
124 device = (DEVRES *)changer->device->first();
125 pm_strcpy(MediaType, device->media_type);
126 bash_spaces(MediaType);
127 /* This is mostly to indicate that we are here */
128 ok = bnet_fsend(dir, Device_update,
130 dev_name.c_str(), /* Changer name */
131 0, 0, 0, /* append, read, num_writers */
132 0, 0, 0, /* is_open, is_labeled, offline */
133 0, 0, /* reserved, max_writers */
135 changer->device->size(), /* Number of devices */
137 "*", /* ChangerName */
138 MediaType.c_str(), /* MediaType */
140 Dmsg1(100, ">dird: %s\n", dir->msg);
147 * Send current JobStatus to Director
149 bool dir_send_job_status(JCR *jcr)
151 return bnet_fsend(jcr->dir_bsock, Job_status, jcr->Job, jcr->JobStatus);
155 * Common routine for:
156 * dir_get_volume_info()
158 * dir_find_next_appendable_volume()
160 * NOTE!!! All calls to this routine must be protected by
161 * locking vol_info_mutex before calling it so that
162 * we don't have one thread modifying the parameters
163 * and another reading them.
165 * Returns: true on success and vol info in dcr->VolCatInfo
168 static bool do_get_volume_info(DCR *dcr)
171 BSOCK *dir = jcr->dir_bsock;
176 if (dir->recv() <= 0) {
177 Dmsg0(200, "getvolname error bnet_recv\n");
178 Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
181 memset(&vol, 0, sizeof(vol));
182 Dmsg2(100, "<dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg);
183 n = sscanf(dir->msg, OK_media, vol.VolCatName,
184 &vol.VolCatJobs, &vol.VolCatFiles,
185 &vol.VolCatBlocks, &vol.VolCatBytes,
186 &vol.VolCatMounts, &vol.VolCatErrors,
187 &vol.VolCatWrites, &vol.VolCatMaxBytes,
188 &vol.VolCatCapacityBytes, vol.VolCatStatus,
189 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
190 &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
191 &vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
192 &vol.LabelType, &vol.VolMediaId);
194 Dmsg4(100, "Bad response from Dir jid=%u fields=%d, len=%d: %s",
195 (uint32_t)jcr->JobId, n, dir->msglen, dir->msg);
196 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
199 vol.InChanger = InChanger; /* bool in structure */
200 unbash_spaces(vol.VolCatName);
201 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
202 dcr->VolCatInfo = vol; /* structure assignment */
204 Dmsg3(100, "do_reqest_vol_info return true jid=%u slot=%d Volume=%s\n",
205 (uint32_t)jcr->JobId, vol.Slot, vol.VolCatName);
211 * Get Volume info for a specific volume from the Director's Database
213 * Returns: true on success (Director guarantees that Pool and MediaType
214 * are correct and VolStatus==Append or
215 * VolStatus==Recycle)
218 * Volume information returned in dcr->VolCatInfo
220 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
223 BSOCK *dir = jcr->dir_bsock;
226 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
227 bash_spaces(dcr->VolCatInfo.VolCatName);
228 dir->fsend(Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
229 writing==GET_VOL_INFO_FOR_WRITE?1:0);
230 Dmsg2(100, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg);
231 unbash_spaces(dcr->VolCatInfo.VolCatName);
232 bool ok = do_get_volume_info(dcr);
240 * Get info on the next appendable volume in the Director's database
242 * Returns: true on success dcr->VolumeName is volume
243 * reserve_volume() called on Volume name
244 * false on failure dcr->VolumeName[0] == 0
245 * also sets dcr->volume_in_use if at least one
246 * in use volume was found.
248 * Volume information returned in dcr
251 bool dir_find_next_appendable_volume(DCR *dcr)
254 BSOCK *dir = jcr->dir_bsock;
257 Dmsg2(200, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n",
258 dcr->reserved_device, dcr->VolumeName);
261 * Try the twenty oldest or most available volumes. Note,
262 * the most available could already be mounted on another
263 * drive, so we continue looking for a not in use Volume.
267 dcr->volume_in_use = false;
268 for (int vol_index=1; vol_index < 20; vol_index++) {
269 bash_spaces(dcr->media_type);
270 bash_spaces(dcr->pool_name);
271 dir->fsend(Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
272 unbash_spaces(dcr->media_type);
273 unbash_spaces(dcr->pool_name);
274 Dmsg2(100, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg);
275 bool ok = do_get_volume_info(dcr);
277 if (!is_volume_in_use(dcr)) {
281 Dmsg2(100, "jid=%u Volume %s is in use.\n", (uint32_t)jcr->JobId, dcr->VolumeName);
282 dcr->volume_in_use = true;
286 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
287 dcr->dev->print_name());
293 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
294 if (reserve_volume(dcr, dcr->VolumeName) == 0) {
295 Dmsg2(100, "Could not reserve volume %s on %s\n", dcr->VolumeName,
296 dcr->dev->print_name());
300 unlock_reservations();
305 dcr->VolumeName[0] = 0;
307 unlock_reservations();
313 * After writing a Volume, send the updated statistics
314 * back to the director. The information comes from the
317 bool dir_update_volume_info(DCR *dcr, bool label)
320 BSOCK *dir = jcr->dir_bsock;
321 DEVICE *dev = dcr->dev;
322 time_t LastWritten = time(NULL);
323 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
324 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
329 /* If system job, do not update catalog */
330 if (jcr->JobType == JT_SYSTEM) {
334 if (vol->VolCatName[0] == 0) {
335 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
336 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
340 /* Lock during Volume update */
342 Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
343 /* Just labeled or relabeled the tape */
345 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
347 pm_strcpy(VolumeName, vol->VolCatName);
348 bash_spaces(VolumeName);
349 InChanger = vol->InChanger;
350 bnet_fsend(dir, Update_media, jcr->Job,
351 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
352 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
353 vol->VolCatMounts, vol->VolCatErrors,
354 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
355 LastWritten, vol->VolCatStatus, vol->Slot, label,
356 InChanger, /* bool in structure */
357 edit_int64(vol->VolReadTime, ed3),
358 edit_int64(vol->VolWriteTime, ed4),
359 edit_uint64(vol->VolFirstWritten, ed5),
361 Dmsg2(100, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg);
363 /* Do not lock device here because it may be locked from label */
364 if (!do_get_volume_info(dcr)) {
365 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
366 Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"),
367 vol->VolCatName, jcr->errmsg);
370 Dmsg2(420, "get_volume_info() jid=%u: %s", (uint32_t)jcr->JobId, dir->msg);
371 /* Update dev Volume info in case something changed (e.g. expired) */
372 dev->VolCatInfo = dcr->VolCatInfo;
381 * After writing a Volume, create the JobMedia record.
383 bool dir_create_jobmedia_record(DCR *dcr)
386 BSOCK *dir = jcr->dir_bsock;
389 /* If system job, do not update catalog */
390 if (jcr->JobType == JT_SYSTEM) {
394 if (!dcr->WroteVol) {
395 return true; /* nothing written to tape */
398 dcr->WroteVol = false;
399 bnet_fsend(dir, Create_job_media, jcr->Job,
400 dcr->VolFirstIndex, dcr->VolLastIndex,
401 dcr->StartFile, dcr->EndFile,
402 dcr->StartBlock, dcr->EndBlock,
403 dcr->Copy, dcr->Stripe,
404 edit_uint64(dcr->VolMediaId, ed1));
405 Dmsg2(100, ">dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg);
406 if (bnet_recv(dir) <= 0) {
407 Dmsg0(190, "create_jobmedia error bnet_recv\n");
408 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
412 Dmsg2(100, "<dird jid=%u: %s", (uint32_t)jcr->JobId, dir->msg);
413 if (strcmp(dir->msg, OK_create) != 0) {
414 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
415 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
423 * Update File Attribute data
425 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
428 BSOCK *dir = jcr->dir_bsock;
431 #ifdef NO_ATTRIBUTES_TEST
435 dir->msg = check_pool_memory_size(dir->msg, sizeof(FileAttributes) +
436 MAX_NAME_LENGTH + sizeof(DEV_RECORD) + rec->data_len + 1);
437 dir->msglen = bsnprintf(dir->msg, sizeof(FileAttributes) +
438 MAX_NAME_LENGTH + 1, FileAttributes, jcr->Job);
439 ser_begin(dir->msg + dir->msglen, 0);
440 ser_uint32(rec->VolSessionId);
441 ser_uint32(rec->VolSessionTime);
442 ser_int32(rec->FileIndex);
443 ser_int32(rec->Stream);
444 ser_uint32(rec->data_len);
445 ser_bytes(rec->data, rec->data_len);
446 dir->msglen = ser_length(dir->msg);
447 Dmsg2(1800, ">dird jid=%u: %s\n", (uint32_t)jcr->JobId, dir->msg); /* Attributes */
453 * Request the sysop to create an appendable volume
455 * Entered with device blocked.
456 * Leaves with device blocked.
458 * Returns: true on success (operator issues a mount command)
460 * Note, must create dev->errmsg on error return.
462 * On success, dcr->VolumeName and dcr->VolCatInfo contain
463 * information on suggested volume, but this may not be the
464 * same as what is actually mounted.
466 * When we return with success, the correct tape may or may not
467 * actually be mounted. The calling routine must read it and
470 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
472 int stat = W_TIMEOUT;
473 DEVICE *dev = dcr->dev;
475 bool got_vol = false;
477 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
478 ASSERT(dev->blocked());
480 if (job_canceled(jcr)) {
482 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
483 jcr->Job, dev->print_name());
484 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
488 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
493 if (stat == W_TIMEOUT || stat == W_MOUNT) {
494 Jmsg(jcr, M_MOUNT, 0, _(
495 "Job %s waiting. Cannot find any appendable volumes.\n"
496 "Please use the \"label\" command to create a new Volume for:\n"
499 " Media type: %s\n"),
507 set_jcr_job_status(jcr, JS_WaitMedia);
508 dir_send_job_status(jcr);
510 stat = wait_for_sysop(dcr);
511 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
513 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
517 if (stat == W_TIMEOUT) {
518 if (!double_dev_wait_time(dev)) {
519 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
520 dev->print_name(), jcr->Job);
521 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
522 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
523 return false; /* exceeded maximum waits */
527 if (stat == W_ERROR) {
529 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
530 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
533 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
535 set_jcr_job_status(jcr, JS_Running);
536 dir_send_job_status(jcr);
537 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
542 * Request to mount specific Volume
544 * Entered with device blocked and dcr->VolumeName is desired
546 * Leaves with device blocked.
548 * Returns: true on success (operator issues a mount command)
550 * Note, must create dev->errmsg on error return.
553 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
555 int stat = W_TIMEOUT;
556 DEVICE *dev = dcr->dev;
559 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
560 if (!dcr->VolumeName[0]) {
561 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
564 ASSERT(dev->blocked());
566 if (job_canceled(jcr)) {
567 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
568 jcr->Job, dev->print_name());
577 * If we are not polling, and the wait timeout or the
578 * user explicitly did a mount, send him the message.
581 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
582 Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" or label a new one for:\n"
586 " Media type: %s\n"),
592 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
593 dcr->VolumeName, dev->print_name(), jcr->Job);
596 set_jcr_job_status(jcr, JS_WaitMount);
597 dir_send_job_status(jcr);
599 stat = wait_for_sysop(dcr); /* wait on device */
600 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
602 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
603 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
607 if (stat == W_TIMEOUT) {
608 if (!double_dev_wait_time(dev)) {
609 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
610 dev->print_name(), jcr->Job);
611 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
612 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
613 return false; /* exceeded maximum waits */
617 if (stat == W_ERROR) {
619 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
620 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
623 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
628 set_jcr_job_status(jcr, JS_Running);
629 dir_send_job_status(jcr);
630 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");