2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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=%s 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=%s VolWriteTime=%s 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 = dir->fsend(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 = dir->fsend(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 jcr->dir_bsock->fsend(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)
170 char ed_vrt[50], ed_vwt[50];
172 BSOCK *dir = jcr->dir_bsock;
177 if (dir->recv() <= 0) {
178 Dmsg0(200, "getvolname error bnet_recv\n");
179 Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
182 memset(&vol, 0, sizeof(vol));
183 Dmsg1(100, "<dird %s", dir->msg);
184 n = sscanf(dir->msg, OK_media, vol.VolCatName,
185 &vol.VolCatJobs, &vol.VolCatFiles,
186 &vol.VolCatBlocks, &vol.VolCatBytes,
187 &vol.VolCatMounts, &vol.VolCatErrors,
188 &vol.VolCatWrites, &vol.VolCatMaxBytes,
189 &vol.VolCatCapacityBytes, vol.VolCatStatus,
190 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
191 &InChanger, ed_vrt, ed_vwt,
192 &vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
193 &vol.LabelType, &vol.VolMediaId);
195 Dmsg3(100, "Bad response from Dir fields=%d, len=%d: %s",
196 n, dir->msglen, dir->msg);
197 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
200 vol.VolReadTime = str_to_int64(ed_vrt);
201 vol.VolWriteTime = str_to_int64(ed_vwt);
202 vol.InChanger = InChanger; /* bool in structure */
203 unbash_spaces(vol.VolCatName);
204 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
205 dcr->VolCatInfo = vol; /* structure assignment */
207 Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n",
208 vol.Slot, vol.VolCatName);
214 * Get Volume info for a specific volume from the Director's Database
216 * Returns: true on success (Director guarantees that Pool and MediaType
217 * are correct and VolStatus==Append or
218 * VolStatus==Recycle)
221 * Volume information returned in dcr->VolCatInfo
223 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
226 BSOCK *dir = jcr->dir_bsock;
229 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
230 bash_spaces(dcr->VolCatInfo.VolCatName);
231 dir->fsend(Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
232 writing==GET_VOL_INFO_FOR_WRITE?1:0);
233 Dmsg1(100, ">dird %s", dir->msg);
234 unbash_spaces(dcr->VolCatInfo.VolCatName);
235 bool ok = do_get_volume_info(dcr);
243 * Get info on the next appendable volume in the Director's database
245 * Returns: true on success dcr->VolumeName is volume
246 * reserve_volume() called on Volume name
247 * false on failure dcr->VolumeName[0] == 0
248 * also sets dcr->volume_in_use if at least one
249 * in use volume was found.
251 * Volume information returned in dcr
254 bool dir_find_next_appendable_volume(DCR *dcr)
257 BSOCK *dir = jcr->dir_bsock;
260 Dmsg2(200, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n",
261 dcr->reserved_device, dcr->VolumeName);
264 * Try the fourty oldest or most available volumes. Note,
265 * the most available could already be mounted on another
266 * drive, so we continue looking for a not in use Volume.
270 dcr->volume_in_use = false;
271 for (int vol_index=1; vol_index < 40; vol_index++) {
272 bash_spaces(dcr->media_type);
273 bash_spaces(dcr->pool_name);
274 dir->fsend(Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
275 unbash_spaces(dcr->media_type);
276 unbash_spaces(dcr->pool_name);
277 Dmsg1(100, ">dird %s", dir->msg);
278 if (do_get_volume_info(dcr)) {
279 if (!is_volume_in_use(dcr)) {
280 Dmsg1(100, "Call reserve_volume. Vol=%s\n", dcr->VolumeName);
281 if (reserve_volume(dcr, dcr->VolumeName) == 0) {
282 Dmsg2(100, "Could not reserve volume %s on %s\n", dcr->VolumeName,
283 dcr->dev->print_name());
286 Dmsg1(100, "dir_find_next_appendable_volume return true. vol=%s\n",
291 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
292 dcr->volume_in_use = true;
296 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
297 dcr->dev->print_name());
301 dcr->VolumeName[0] = 0;
311 * After writing a Volume, send the updated statistics
312 * back to the director. The information comes from the
315 bool dir_update_volume_info(DCR *dcr, bool label, bool update_LastWritten)
318 BSOCK *dir = jcr->dir_bsock;
319 DEVICE *dev = dcr->dev;
320 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
321 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50];
326 /* If system job, do not update catalog */
327 if (jcr->JobType == JT_SYSTEM) {
331 if (vol->VolCatName[0] == 0) {
332 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
333 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
337 /* Lock during Volume update */
339 Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
340 /* Just labeled or relabeled the tape */
342 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
344 // if (update_LastWritten) {
345 vol->VolLastWritten = time(NULL);
347 pm_strcpy(VolumeName, vol->VolCatName);
348 bash_spaces(VolumeName);
349 InChanger = vol->InChanger;
350 dir->fsend(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 edit_uint64(vol->VolLastWritten, ed6),
356 vol->VolCatStatus, vol->Slot, label,
357 InChanger, /* bool in structure */
358 edit_int64(vol->VolReadTime, ed3),
359 edit_int64(vol->VolWriteTime, ed4),
360 edit_uint64(vol->VolFirstWritten, ed5),
362 Dmsg1(100, ">dird %s", dir->msg);
364 /* Do not lock device here because it may be locked from label */
365 if (!do_get_volume_info(dcr)) {
366 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
367 Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"),
368 vol->VolCatName, jcr->errmsg);
371 Dmsg1(420, "get_volume_info() %s", dir->msg);
372 /* Update dev Volume info in case something changed (e.g. expired) */
373 dev->VolCatInfo = dcr->VolCatInfo;
382 * After writing a Volume, create the JobMedia record.
384 bool dir_create_jobmedia_record(DCR *dcr)
387 BSOCK *dir = jcr->dir_bsock;
390 /* If system job, do not update catalog */
391 if (jcr->JobType == JT_SYSTEM) {
395 if (!dcr->WroteVol) {
396 return true; /* nothing written to tape */
399 dcr->WroteVol = false;
400 dir->fsend(Create_job_media, jcr->Job,
401 dcr->VolFirstIndex, dcr->VolLastIndex,
402 dcr->StartFile, dcr->EndFile,
403 dcr->StartBlock, dcr->EndBlock,
404 dcr->Copy, dcr->Stripe,
405 edit_uint64(dcr->VolMediaId, ed1));
406 Dmsg1(100, ">dird %s", dir->msg);
407 if (dir->recv() <= 0) {
408 Dmsg0(190, "create_jobmedia error bnet_recv\n");
409 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
413 Dmsg1(100, "<dird %s", dir->msg);
414 if (strcmp(dir->msg, OK_create) != 0) {
415 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
416 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
424 * Update File Attribute data
426 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
429 BSOCK *dir = jcr->dir_bsock;
432 #ifdef NO_ATTRIBUTES_TEST
436 dir->msg = check_pool_memory_size(dir->msg, sizeof(FileAttributes) +
437 MAX_NAME_LENGTH + sizeof(DEV_RECORD) + rec->data_len + 1);
438 dir->msglen = bsnprintf(dir->msg, sizeof(FileAttributes) +
439 MAX_NAME_LENGTH + 1, FileAttributes, jcr->Job);
440 ser_begin(dir->msg + dir->msglen, 0);
441 ser_uint32(rec->VolSessionId);
442 ser_uint32(rec->VolSessionTime);
443 ser_int32(rec->FileIndex);
444 ser_int32(rec->Stream);
445 ser_uint32(rec->data_len);
446 ser_bytes(rec->data, rec->data_len);
447 dir->msglen = ser_length(dir->msg);
448 Dmsg1(1800, ">dird %s\n", dir->msg); /* Attributes */
454 * Request the sysop to create an appendable volume
456 * Entered with device blocked.
457 * Leaves with device blocked.
459 * Returns: true on success (operator issues a mount command)
461 * Note, must create dev->errmsg on error return.
463 * On success, dcr->VolumeName and dcr->VolCatInfo contain
464 * information on suggested volume, but this may not be the
465 * same as what is actually mounted.
467 * When we return with success, the correct tape may or may not
468 * actually be mounted. The calling routine must read it and
471 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
473 int stat = W_TIMEOUT;
474 DEVICE *dev = dcr->dev;
476 bool got_vol = false;
478 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
479 ASSERT(dev->blocked());
481 if (job_canceled(jcr)) {
483 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
484 jcr->Job, dev->print_name());
485 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
489 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
494 if (stat == W_TIMEOUT || stat == W_MOUNT) {
495 Jmsg(jcr, M_MOUNT, 0, _(
496 "Job %s waiting. Cannot find any appendable volumes.\n"
497 "Please use the \"label\" command to create a new Volume for:\n"
500 " Media type: %s\n"),
508 set_jcr_job_status(jcr, JS_WaitMedia);
509 dir_send_job_status(jcr);
511 stat = wait_for_sysop(dcr);
512 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
514 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
518 if (stat == W_TIMEOUT) {
519 if (!double_dev_wait_time(dev)) {
520 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
521 dev->print_name(), jcr->Job);
522 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
523 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
524 return false; /* exceeded maximum waits */
528 if (stat == W_ERROR) {
530 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
531 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
534 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
536 set_jcr_job_status(jcr, JS_Running);
537 dir_send_job_status(jcr);
538 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
543 * Request to mount specific Volume
545 * Entered with device blocked and dcr->VolumeName is desired
547 * Leaves with device blocked.
549 * Returns: true on success (operator issues a mount command)
551 * Note, must create dev->errmsg on error return.
554 bool dir_ask_sysop_to_mount_volume(DCR *dcr, int mode)
556 int stat = W_TIMEOUT;
557 DEVICE *dev = dcr->dev;
560 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
561 if (!dcr->VolumeName[0]) {
562 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
565 ASSERT(dev->blocked());
567 if (job_canceled(jcr)) {
568 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
569 jcr->Job, dev->print_name());
578 * If we are not polling, and the wait timeout or the
579 * user explicitly did a mount, send him the message.
582 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
584 if (mode == ST_APPEND) {
585 msg = _("Please mount Volume \"%s\" or label a new one for:\n"
589 " Media type: %s\n");
591 msg = _("Please mount Volume \"%s\" for:\n"
595 " Media type: %s\n");
597 Jmsg(jcr, M_MOUNT, 0, msg,
603 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
604 dcr->VolumeName, dev->print_name(), jcr->Job);
607 set_jcr_job_status(jcr, JS_WaitMount);
608 dir_send_job_status(jcr);
610 stat = wait_for_sysop(dcr); /* wait on device */
611 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
613 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
614 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
618 if (stat == W_TIMEOUT) {
619 if (!double_dev_wait_time(dev)) {
620 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
621 dev->print_name(), jcr->Job);
622 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
623 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
624 return false; /* exceeded maximum waits */
628 if (stat == W_ERROR) {
630 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
631 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
634 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
639 set_jcr_job_status(jcr, JS_Running);
640 dir_send_job_status(jcr);
641 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");