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 plus additions
11 that are listed in the file LICENSE.
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 (bnet_recv(dir) <= 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 Dmsg1(100, "<dird %s", 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 Dmsg2(100, "Bad response from Dir fields=%d: %s", n, dir->msg);
195 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
198 vol.InChanger = InChanger; /* bool in structure */
199 unbash_spaces(vol.VolCatName);
200 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
201 dcr->VolCatInfo = vol; /* structure assignment */
203 Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n",
204 vol.Slot, vol.VolCatName);
210 * Get Volume info for a specific volume from the Director's Database
212 * Returns: true on success (Director guarantees that Pool and MediaType
213 * are correct and VolStatus==Append or
214 * VolStatus==Recycle)
217 * Volume information returned in dcr->VolCatInfo
219 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
222 BSOCK *dir = jcr->dir_bsock;
225 bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
226 bash_spaces(dcr->VolCatInfo.VolCatName);
227 bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
228 writing==GET_VOL_INFO_FOR_WRITE?1:0);
229 Dmsg1(100, ">dird: %s", dir->msg);
230 unbash_spaces(dcr->VolCatInfo.VolCatName);
231 bool ok = do_get_volume_info(dcr);
239 * Get info on the next appendable volume in the Director's database
241 * Returns: true on success dcr->VolumeName is volume
242 * reserve_volume() called on Volume name
243 * false on failure dcr->VolumeName[0] == 0
245 * Volume information returned in dcr
248 bool dir_find_next_appendable_volume(DCR *dcr)
251 BSOCK *dir = jcr->dir_bsock;
254 Dmsg0(200, "dir_find_next_appendable_volume\n");
256 * Try the twenty oldest or most available volumes. Note,
257 * the most available could already be mounted on another
258 * drive, so we continue looking for a not in use Volume.
262 for (int vol_index=1; vol_index < 20; vol_index++) {
263 bash_spaces(dcr->media_type);
264 bash_spaces(dcr->pool_name);
265 bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
266 unbash_spaces(dcr->media_type);
267 unbash_spaces(dcr->pool_name);
268 Dmsg1(100, ">dird: %s", dir->msg);
269 bool ok = do_get_volume_info(dcr);
271 if (!is_volume_in_use(dcr)) {
275 Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
279 Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
280 dcr->dev->print_name());
286 Dmsg0(400, "dir_find_next_appendable_volume return true\n");
287 if (reserve_volume(dcr, dcr->VolumeName) == 0) {
288 Dmsg2(100, "Could not reserve volume %s on %s\n", dcr->VolumeName,
289 dcr->dev->print_name());
293 unlock_reservations();
298 dcr->VolumeName[0] = 0;
300 unlock_reservations();
306 * After writing a Volume, send the updated statistics
307 * back to the director. The information comes from the
310 bool dir_update_volume_info(DCR *dcr, bool label)
313 BSOCK *dir = jcr->dir_bsock;
314 DEVICE *dev = dcr->dev;
315 time_t LastWritten = time(NULL);
316 VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
317 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
322 /* If system job, do not update catalog */
323 if (jcr->JobType == JT_SYSTEM) {
327 if (vol->VolCatName[0] == 0) {
328 Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
329 Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
333 /* Lock during Volume update */
335 Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
336 /* Just labeled or relabeled the tape */
338 bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
340 pm_strcpy(VolumeName, vol->VolCatName);
341 bash_spaces(VolumeName);
342 InChanger = vol->InChanger;
343 bnet_fsend(dir, Update_media, jcr->Job,
344 VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
345 vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
346 vol->VolCatMounts, vol->VolCatErrors,
347 vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
348 LastWritten, vol->VolCatStatus, vol->Slot, label,
349 InChanger, /* bool in structure */
350 edit_int64(vol->VolReadTime, ed3),
351 edit_int64(vol->VolWriteTime, ed4),
352 edit_uint64(vol->VolFirstWritten, ed5),
354 Dmsg1(100, ">dird: %s", dir->msg);
356 /* Do not lock device here because it may be locked from label */
357 if (!do_get_volume_info(dcr)) {
358 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
359 Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"),
360 vol->VolCatName, jcr->errmsg);
363 Dmsg1(420, "get_volume_info(): %s", dir->msg);
364 /* Update dev Volume info in case something changed (e.g. expired) */
365 dev->VolCatInfo = dcr->VolCatInfo;
374 * After writing a Volume, create the JobMedia record.
376 bool dir_create_jobmedia_record(DCR *dcr)
379 BSOCK *dir = jcr->dir_bsock;
382 /* If system job, do not update catalog */
383 if (jcr->JobType == JT_SYSTEM) {
387 if (!dcr->WroteVol) {
388 return true; /* nothing written to tape */
391 dcr->WroteVol = false;
392 bnet_fsend(dir, Create_job_media, jcr->Job,
393 dcr->VolFirstIndex, dcr->VolLastIndex,
394 dcr->StartFile, dcr->EndFile,
395 dcr->StartBlock, dcr->EndBlock,
396 dcr->Copy, dcr->Stripe,
397 edit_uint64(dcr->dev->VolCatInfo.VolMediaId, ed1));
398 Dmsg1(100, ">dird: %s", dir->msg);
399 if (bnet_recv(dir) <= 0) {
400 Dmsg0(190, "create_jobmedia error bnet_recv\n");
401 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
405 Dmsg1(100, "<dir: %s", dir->msg);
406 if (strcmp(dir->msg, OK_create) != 0) {
407 Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
408 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
416 * Update File Attribute data
418 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
421 BSOCK *dir = jcr->dir_bsock;
424 #ifdef NO_ATTRIBUTES_TEST
428 dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
429 dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
430 sizeof(DEV_RECORD) + rec->data_len);
431 ser_begin(dir->msg + dir->msglen, 0);
432 ser_uint32(rec->VolSessionId);
433 ser_uint32(rec->VolSessionTime);
434 ser_int32(rec->FileIndex);
435 ser_int32(rec->Stream);
436 ser_uint32(rec->data_len);
437 ser_bytes(rec->data, rec->data_len);
438 dir->msglen = ser_length(dir->msg);
439 Dmsg1(1800, ">dird: %s\n", dir->msg); /* Attributes */
440 return bnet_send(dir);
445 * Request the sysop to create an appendable volume
447 * Entered with device blocked.
448 * Leaves with device blocked.
450 * Returns: true on success (operator issues a mount command)
452 * Note, must create dev->errmsg on error return.
454 * On success, dcr->VolumeName and dcr->VolCatInfo contain
455 * information on suggested volume, but this may not be the
456 * same as what is actually mounted.
458 * When we return with success, the correct tape may or may not
459 * actually be mounted. The calling routine must read it and
462 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
464 int stat = W_TIMEOUT;
465 DEVICE *dev = dcr->dev;
467 bool got_vol = false;
469 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
470 ASSERT(dev->blocked());
472 if (job_canceled(jcr)) {
474 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
475 jcr->Job, dev->print_name());
476 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
480 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
485 if (stat == W_TIMEOUT || stat == W_MOUNT) {
486 Jmsg(jcr, M_MOUNT, 0, _(
487 "Job %s waiting. Cannot find any appendable volumes.\n"
488 "Please use the \"label\" command to create a new Volume for:\n"
499 set_jcr_job_status(jcr, JS_WaitMedia);
500 dir_send_job_status(jcr);
502 stat = wait_for_sysop(dcr);
503 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
505 Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
509 if (stat == W_TIMEOUT) {
510 if (!double_dev_wait_time(dev)) {
511 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
512 dev->print_name(), jcr->Job);
513 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
514 Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
515 return false; /* exceeded maximum waits */
519 if (stat == W_ERROR) {
521 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
522 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
525 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
527 set_jcr_job_status(jcr, JS_Running);
528 dir_send_job_status(jcr);
529 Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
534 * Request to mount specific Volume
536 * Entered with device blocked and dcr->VolumeName is desired
538 * Leaves with device blocked.
540 * Returns: true on success (operator issues a mount command)
542 * Note, must create dev->errmsg on error return.
545 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
547 int stat = W_TIMEOUT;
548 DEVICE *dev = dcr->dev;
551 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
552 if (!dcr->VolumeName[0]) {
553 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
556 ASSERT(dev->blocked());
558 if (job_canceled(jcr)) {
559 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
560 jcr->Job, dev->print_name());
569 * If we are not polling, and the wait timeout or the
570 * user explicitly did a mount, send him the message.
573 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
574 Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
575 dcr->VolumeName, dev->print_name(), jcr->Job);
576 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
577 dcr->VolumeName, dev->print_name(), jcr->Job);
580 set_jcr_job_status(jcr, JS_WaitMount);
581 dir_send_job_status(jcr);
583 stat = wait_for_sysop(dcr); /* wait on device */
584 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
586 Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
587 Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
591 if (stat == W_TIMEOUT) {
592 if (!double_dev_wait_time(dev)) {
593 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
594 dev->print_name(), jcr->Job);
595 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
596 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
597 return false; /* exceeded maximum waits */
601 if (stat == W_ERROR) {
603 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
604 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
607 Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
610 set_jcr_job_status(jcr, JS_Running);
611 dir_send_job_status(jcr);
612 Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");