1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * Subroutines to handle Catalog reqests sent to the Director
21 * Reqests/commands from the Director are handled in dircmd.c
22 *
23 * Kern Sibbald, December 2000
24 */
25
26 #include "bacula.h" /* pull in global headers */
27 #include "stored.h" /* pull in Storage Deamon headers */
28
29 static const int dbglvl = 200;
30
31 /* Requests sent to the Director */
32 static char Find_media[] = "CatReq JobId=%ld FindMedia=%d pool_name=%s media_type=%s vol_type=%d create=%d\n";
33 static char Get_Vol_Info[] = "CatReq JobId=%ld GetVolInfo VolName=%s write=%d\n";
34 static char Update_media[] = "CatReq JobId=%ld UpdateMedia VolName=%s"
35 " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%s VolABytes=%s"
36 " VolHoleBytes=%s VolHoles=%u VolMounts=%u"
37 " VolErrors=%u VolWrites=%u MaxVolBytes=%s EndTime=%s VolStatus=%s"
38 " Slot=%d relabel=%d InChanger=%d VolReadTime=%s VolWriteTime=%s"
39 " VolFirstWritten=%s VolType=%u VolParts=%d VolCloudParts=%d"
40 " LastPartBytes=%lld Enabled=%d Recycle=%d\n";
41 static char Create_jobmedia[] = "CatReq JobId=%ld CreateJobMedia\n";
42 static char FileAttributes[] = "UpdCat JobId=%ld FileAttributes ";
43
44 /* Responses received from the Director */
45 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
46 " VolBlocks=%lu VolBytes=%lld VolABytes=%lld"
47 " VolHoleBytes=%lld VolHoles=%lu VolMounts=%lu"
48 " VolErrors=%lu VolWrites=%lu"
49 " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
50 " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
51 " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
52 " VolType=%lu LabelType=%ld MediaId=%lld ScratchPoolId=%lld"
53 " VolParts=%d VolCloudParts=%d LastPartBytes=%lld Enabled=%d MaxPoolBytes=%lld PoolBytes=%lld Recycle=%d\n";
54
55
56 static char OK_create[] = "1000 OK CreateJobMedia\n";
57
58 static bthread_mutex_t vol_info_mutex = BTHREAD_MUTEX_PRIORITY(PRIO_SD_VOL_INFO);
59
60 #ifdef needed
61
62 Note: if you turn this on, be sure to add the Recycle Flag
63
64 static char Device_update[] = "DevUpd JobId=%ld device=%s "
65 "append=%d read=%d num_writers=%d "
66 "open=%d labeled=%d offline=%d "
67 "reserved=%d max_writers=%d "
68 "autoselect=%d autochanger=%d "
69 "enabled=%d "
70 "changer_name=%s media_type=%s volume_name=%s\n";
71
72
73 /** Send update information about a device to Director */
dir_update_device(JCR * jcr,DEVICE * dev)74 bool dir_update_device(JCR *jcr, DEVICE *dev)
75 {
76 BSOCK *dir = jcr->dir_bsock;
77 POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
78 DEVRES *device = dev->device;
79 bool ok;
80
81 pm_strcpy(dev_name, device->hdr.name);
82 bash_spaces(dev_name);
83 if (dev->is_labeled()) {
84 pm_strcpy(VolumeName, dev->VolHdr.VolumeName);
85 } else {
86 pm_strcpy(VolumeName, "*");
87 }
88 bash_spaces(VolumeName);
89 pm_strcpy(MediaType, device->media_type);
90 bash_spaces(MediaType);
91 if (device->changer_res) {
92 pm_strcpy(ChangerName, device->changer_res->hdr.name);
93 bash_spaces(ChangerName);
94 } else {
95 pm_strcpy(ChangerName, "*");
96 }
97 ok = dir->fsend(Device_update,
98 jcr->JobId,
99 dev_name.c_str(),
100 dev->can_append()!=0,
101 dev->can_read()!=0, dev->num_writers,
102 dev->is_open()!=0, dev->is_labeled()!=0,
103 dev->is_offline()!=0, dev->reserved_device,
104 dev->is_tape()?100000:1,
105 dev->autoselect, 0,
106 dev->enabled,
107 ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
108 Dmsg1(dbglvl, ">dird: %s\n", dir->msg);
109 return ok;
110 }
111
dir_update_changer(JCR * jcr,AUTOCHANGER * changer)112 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
113 {
114 BSOCK *dir = jcr->dir_bsock;
115 POOL_MEM dev_name, MediaType;
116 DEVRES *device;
117 bool ok;
118
119 pm_strcpy(dev_name, changer->hdr.name);
120 bash_spaces(dev_name);
121 device = (DEVRES *)changer->device->first();
122 pm_strcpy(MediaType, device->media_type);
123 bash_spaces(MediaType);
124 /* This is mostly to indicate that we are here */
125 ok = dir->fsend(Device_update,
126 jcr->JobId,
127 dev_name.c_str(), /* Changer name */
128 0, 0, 0, /* append, read, num_writers */
129 0, 0, 0, /* is_open, is_labeled, offline */
130 0, 0, /* reserved, max_writers */
131 0, /* Autoselect */
132 0, /* Enabled */
133 changer->device->size(), /* Number of devices */
134 "0", /* PoolId */
135 "*", /* ChangerName */
136 MediaType.c_str(), /* MediaType */
137 "*"); /* VolName */
138 Dmsg1(dbglvl, ">dird: %s\n", dir->msg);
139 return ok;
140 }
141 #endif
142
143
144 static AskDirHandler *askdir_handler = NULL; /* must be true when inside a "btools" */
145
get_askdir_handler()146 AskDirHandler *get_askdir_handler()
147 {
148 return askdir_handler;
149 }
150
151 /*
152 * btools must call this function, to modify behavior of some functions here
153 */
init_askdir_handler(AskDirHandler * new_askdir_handler)154 AskDirHandler *init_askdir_handler(AskDirHandler *new_askdir_handler)
155 {
156 AskDirHandler *old = askdir_handler;
157 askdir_handler = new_askdir_handler;
158 return old;
159 }
160
161 /*
162 * Alternate function used by btools
163 */
dir_ask_sysop_to_mount_volume(DCR * dcr,bool)164 bool AskDirHandler::dir_ask_sysop_to_mount_volume(DCR *dcr, bool /*writing*/)
165 {
166 DEVICE *dev = dcr->dev;
167 fprintf(stderr, _("Mount Volume \"%s\" on device %s and press return when ready: "),
168 dcr->VolumeName, dev->print_name());
169 dev->close(dcr);
170 getchar();
171 return true;
172 }
173
dir_get_volume_info(DCR * dcr,const char * VolumeName,enum get_vol_info_rw writing)174 bool AskDirHandler::dir_get_volume_info(DCR *dcr, const char *VolumeName, enum get_vol_info_rw writing)
175 {
176 Dmsg0(100, "Fake dir_get_volume_info\n");
177 dcr->setVolCatName(VolumeName);
178 Dmsg2(500, "Vol=%s VolType=%d\n", dcr->getVolCatName(), dcr->VolCatInfo.VolCatType);
179 return 1;
180 }
181
182 /**
183 * Send current JobStatus to Director
184 */
dir_send_job_status(JCR * jcr)185 bool dir_send_job_status(JCR *jcr)
186 {
187 if (askdir_handler) {
188 return askdir_handler->dir_send_job_status(jcr);
189 }
190
191 return jcr->sendJobStatus();
192 }
193
194 /**
195 * Common routine for:
196 * dir_get_volume_info()
197 * and
198 * dir_find_next_appendable_volume()
199 *
200 * NOTE!!! All calls to this routine must be protected by
201 * locking vol_info_mutex before calling it so that
202 * we don't have one thread modifying the parameters
203 * and another reading them.
204 *
205 * Returns: true on success and vol info in dcr->VolCatInfo
206 * false on failure
207 */
do_get_volume_info(DCR * dcr)208 static bool do_get_volume_info(DCR *dcr)
209 {
210 JCR *jcr = dcr->jcr;
211 BSOCK *dir = jcr->dir_bsock;
212 VOLUME_CAT_INFO vol;
213 int n;
214 int32_t Enabled, Recycle;
215 int32_t InChanger;
216
217 dcr->setVolCatInfo(false);
218 if (dir->recv() <= 0) {
219 Dmsg0(dbglvl, "getvolname error bnet_recv\n");
220 Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
221 return false;
222 }
223 memset(&vol, 0, sizeof(vol));
224 n = sscanf(dir->msg, OK_media, vol.VolCatName,
225 &vol.VolCatJobs, &vol.VolCatFiles,
226 &vol.VolCatBlocks, &vol.VolCatAmetaBytes,
227 &vol.VolCatAdataBytes, &vol.VolCatHoleBytes,
228 &vol.VolCatHoles, &vol.VolCatMounts, &vol.VolCatErrors,
229 &vol.VolCatWrites, &vol.VolCatMaxBytes,
230 &vol.VolCatCapacityBytes, vol.VolCatStatus,
231 &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
232 &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
233 &vol.EndFile, &vol.EndBlock, &vol.VolCatType,
234 &vol.LabelType, &vol.VolMediaId, &vol.VolScratchPoolId,
235 &vol.VolCatParts, &vol.VolCatCloudParts,
236 &vol.VolLastPartBytes, &Enabled, &vol.MaxPoolBytes, &vol.PoolBytes, &Recycle);
237 Dmsg2(dbglvl, "<dird n=%d %s", n, dir->msg);
238 if (n != 33) {
239 Dmsg1(dbglvl, "get_volume_info failed: ERR=%s", dir->msg);
240 /*
241 * Note, we can get an error here either because there is
242 * a comm problem, or if the volume is not a suitable
243 * volume to use, so do not issue a Jmsg() here, do it
244 * in the calling routine.
245 */
246 Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
247 return false;
248 }
249 vol.InChanger = InChanger; /* bool in structure */
250 vol.VolEnabled = Enabled; /* bool in structure */
251 vol.VolRecycle = Recycle; /* bool in structure */
252 vol.is_valid = true;
253 vol.VolCatBytes = vol.VolCatAmetaBytes + vol.VolCatAdataBytes;
254 unbash_spaces(vol.VolCatName);
255 bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
256 dcr->VolCatInfo = vol; /* structure assignment */
257
258 Dmsg3(dbglvl, "do_reqest_vol_info return true slot=%d Volume=%s MediaId=%lld\n",
259 dcr->VolCatInfo.Slot, dcr->VolCatInfo.VolCatName, dcr->VolCatInfo.VolMediaId);
260 Dmsg5(dbglvl, "Dir returned VolCatAmetaBytes=%lld VolCatAdataBytes=%lld Status=%s Vol=%s MediaId=%lld\n",
261 dcr->VolCatInfo.VolCatAmetaBytes, dcr->VolCatInfo.VolCatAdataBytes,
262 dcr->VolCatInfo.VolCatStatus, dcr->VolCatInfo.VolCatName,
263 dcr->VolCatInfo.VolMediaId);
264 return true;
265 }
266
267
268 /**
269 * Get Volume info for a specific volume from the Director's Database
270 *
271 * Returns: true on success (Director guarantees that Pool and MediaType
272 * are correct and VolStatus==Append or
273 * VolStatus==Recycle)
274 * false on failure
275 *
276 * Volume information returned in dcr->VolCatInfo
277 */
dir_get_volume_info(DCR * dcr,const char * VolumeName,enum get_vol_info_rw writing)278 bool dir_get_volume_info(DCR *dcr,
279 const char *VolumeName,
280 enum get_vol_info_rw writing)
281 {
282 if (askdir_handler) {
283 return askdir_handler->dir_get_volume_info(dcr, VolumeName, writing);
284 }
285
286 JCR *jcr = dcr->jcr;
287 BSOCK *dir = jcr->dir_bsock;
288
289 P(vol_info_mutex);
290 dcr->setVolCatName(VolumeName);
291 bash_spaces(dcr->getVolCatName());
292 dir->fsend(Get_Vol_Info, jcr->JobId, dcr->getVolCatName(),
293 writing==GET_VOL_INFO_FOR_WRITE?1:0);
294 Dmsg1(dbglvl, ">dird %s", dir->msg);
295 unbash_spaces(dcr->getVolCatName());
296 bool ok = do_get_volume_info(dcr);
297 V(vol_info_mutex);
298 return ok;
299 }
300
301
302
303 /**
304 * Get info on the next appendable volume in the Director's database
305 *
306 * Returns: true on success dcr->VolumeName is volume
307 * reserve_volume() called on Volume name
308 * false on failure dcr->VolumeName[0] == 0
309 * also sets dcr->found_in_use if at least one
310 * in use volume was found.
311 *
312 * Volume information returned in dcr
313 *
314 */
dir_find_next_appendable_volume(DCR * dcr)315 bool dir_find_next_appendable_volume(DCR *dcr)
316 {
317 /* SD tools setup a handler because they have no connection to Dir */
318 if (askdir_handler) {
319 return askdir_handler->dir_find_next_appendable_volume(dcr);
320 }
321
322 JCR *jcr = dcr->jcr;
323 BSOCK *dir = jcr->dir_bsock;
324 bool rtn;
325 char lastVolume[MAX_NAME_LENGTH];
326
327 /* We may retrieve N volumes that are all busy if we have N busy devices
328 * on the system
329 */
330 int nb_retry=((rblist *)res_head[R_DEVICE-r_first]->res_list)->size() + 30;
331
332 Dmsg2(dbglvl, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n",
333 dcr->is_reserved(), dcr->VolumeName);
334 Mmsg(jcr->errmsg, "Unknown error\n");
335
336 /*
337 * Try the thirty oldest or most available volumes. Note,
338 * the most available could already be mounted on another
339 * drive, so we continue looking for a not in use Volume.
340 */
341 lock_volumes();
342 P(vol_info_mutex);
343 dcr->clear_found_in_use();
344 lastVolume[0] = 0;
345 for (int vol_index=1; vol_index < nb_retry; vol_index++) {
346 /* Have we still some space to create a new volume? */
347 bool can_create = dcr->dev->is_nospace()==false;
348 bash_spaces(dcr->media_type);
349 bash_spaces(dcr->pool_name);
350 dir->fsend(Find_media, jcr->JobId, vol_index, dcr->pool_name, dcr->media_type,
351 dcr->dev->dev_type, can_create);
352 unbash_spaces(dcr->media_type);
353 unbash_spaces(dcr->pool_name);
354 Dmsg1(dbglvl, ">dird %s", dir->msg);
355 if (do_get_volume_info(dcr)) {
356 /* Give up if we get the same volume name twice */
357 if (lastVolume[0] && strcmp(lastVolume, dcr->VolumeName) == 0) {
358 Mmsg(jcr->errmsg, "Director returned same volume name=%s twice.\n",
359 lastVolume);
360 Dmsg1(dbglvl, "Got same vol = %s\n", lastVolume);
361 break;
362 }
363 /* If VolCatAdataBytes, we have ALIGNED_DEV */
364 if (dcr->VolCatInfo.VolCatType == 0 && dcr->VolCatInfo.VolCatAdataBytes != 0) {
365 dcr->VolCatInfo.VolCatType = B_ALIGNED_DEV;
366 }
367 /*
368 * If we have VolType and we are disk or aligned, the VolType must match
369 */
370 /* ***FIXME*** find better way to handle voltype */
371 if (dcr->VolCatInfo.VolCatType != 0 &&
372 (dcr->dev->dev_type == B_FILE_DEV || dcr->dev->dev_type == B_ALIGNED_DEV ||
373 dcr->dev->dev_type == B_CLOUD_DEV) &&
374 dcr->dev->dev_type != (int)dcr->VolCatInfo.VolCatType) {
375 Dmsg2(000, "Skip vol. Wanted VolType=%d Got=%d\n", dcr->dev->dev_type, dcr->VolCatInfo.VolCatType);
376 continue;
377 }
378 /* Dedup devices is a bit special, we have two kind of device type so we accept both */
379 if (dcr->VolCatInfo.VolCatType != 0 && dcr->dev->dev_type == B_DEDUP_DEV &&
380 (B_DEDUP_DEV != (int)dcr->VolCatInfo.VolCatType &&
381 B_DEDUP_OLD_DEV != (int)dcr->VolCatInfo.VolCatType))
382 {
383 Dmsg2(000, "Skip vol. Wanted VolType=%d Got=%d\n", dcr->dev->dev_type, dcr->VolCatInfo.VolCatType);
384 continue;
385 }
386 bstrncpy(lastVolume, dcr->VolumeName, sizeof(lastVolume));
387 if (dcr->can_i_write_volume()) {
388 Dmsg1(dbglvl, "Call reserve_volume for write. Vol=%s\n", dcr->VolumeName);
389 if (reserve_volume(dcr, dcr->VolumeName) == NULL) {
390 Dmsg1(dbglvl, "%s", jcr->errmsg);
391 if (dcr->dev->must_wait()) {
392 rtn = false;
393 dcr->VolumeName[0] = 0;
394 goto get_out;
395 }
396 continue;
397 }
398 Dmsg1(dbglvl, "dir_find_next_appendable_volume return true. vol=%s\n",
399 dcr->VolumeName);
400 rtn = true;
401 goto get_out;
402 } else {
403 Mmsg(jcr->errmsg, "Volume %s is in use.\n", dcr->VolumeName);
404 Dmsg1(dbglvl, "Volume %s is in use.\n", dcr->VolumeName);
405 /* If volume is not usable, it is in use by someone else */
406 dcr->set_found_in_use();
407 continue;
408 }
409 }
410 Dmsg2(dbglvl, "No vol. index %d return false. dev=%s\n", vol_index,
411 dcr->dev->print_name());
412 break;
413 }
414 rtn = false;
415 dcr->VolumeName[0] = 0;
416
417 get_out:
418 V(vol_info_mutex);
419 unlock_volumes();
420 if (!rtn && dcr->VolCatInfo.VolScratchPoolId != 0) {
421 Jmsg(jcr, M_WARNING, 0, "%s", jcr->errmsg);
422 Dmsg2(000, "!!!!!!!!! Volume=%s rejected ScratchPoolId=%lld\n", dcr->VolumeName,
423 dcr->VolCatInfo.VolScratchPoolId);
424 Dmsg1(000, "%s", jcr->errmsg);
425 //} else {
426 // Dmsg3(000, "Rtn=%d Volume=%s ScratchPoolId=%lld\n", rtn, dcr->VolumeName,
427 // dcr->VolCatInfo.VolScratchPoolId);
428 }
429 return rtn;
430 }
431
432
433 /*
434 * After writing a Volume, send the updated statistics
435 * back to the director. The information comes from the
436 * dev record.
437 */
dir_update_volume_info(DCR * dcr,bool label,bool update_LastWritten,bool use_dcr_only)438 bool dir_update_volume_info(DCR *dcr, bool label, bool update_LastWritten,
439 bool use_dcr_only)
440 {
441 if (askdir_handler) {
442 return askdir_handler->dir_update_volume_info(dcr, label, update_LastWritten, use_dcr_only);
443 }
444
445 JCR *jcr = dcr->jcr;
446 BSOCK *dir = jcr->dir_bsock;
447 DEVICE *dev = dcr->ameta_dev;
448 VOLUME_CAT_INFO vol;
449 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50], ed6[50], ed7[50], ed8[50];
450 int InChanger, Enabled, Recycle;
451 bool ok = false;
452 POOL_MEM VolumeName;
453
454 /* If system job, do not update catalog, except if we explicitly force it. */
455 if (jcr->getJobType() == JT_SYSTEM &&
456 !dcr->force_update_volume_info) {
457 return true;
458 }
459
460 /* Lock during Volume update */
461 P(vol_info_mutex);
462 dev->Lock_VolCatInfo();
463
464 if (use_dcr_only) {
465 vol = dcr->VolCatInfo; /* structure assignment */
466 } else {
467 /* Just labeled or relabeled the tape */
468 if (label) {
469 dev->setVolCatStatus("Append");
470 }
471 vol = dev->VolCatInfo; /* structure assignment */
472 /* Reset the number of bytes written since the last update */
473 dev->VolCatInfo.BytesWritten = 0;
474 }
475
476 /* This happens when nothing to update after fixup_device ... */
477 if (vol.VolCatName[0] == 0) {
478 Dmsg0(50, "Volume Name is NULL\n");
479 goto bail_out;
480 }
481 Dmsg4(100, "Update cat VolBytes=%lld VolABytes=%lld Status=%s Vol=%s\n",
482 vol.VolCatAmetaBytes, vol.VolCatAdataBytes, vol.VolCatStatus, vol.VolCatName);
483 // if (update_LastWritten) {
484 vol.VolLastWritten = time(NULL);
485 // }
486 /* worm cannot be recycled, ensure catalog correct */
487 if (dev->is_worm() && vol.VolRecycle) {
488 Jmsg(jcr, M_INFO, 0, _("WORM cassette detected: setting Recycle=No on Volume=\"%s\"\n"), vol.VolCatName);
489 vol.VolRecycle = false;
490 }
491 pm_strcpy(VolumeName, vol.VolCatName);
492 bash_spaces(VolumeName);
493 InChanger = vol.InChanger;
494 Enabled = vol.VolEnabled;
495 Recycle = vol.VolRecycle;
496 /* Insanity test */
497 if (vol.VolCatHoleBytes > (((uint64_t)2)<<60)) {
498 Pmsg1(010, "VolCatHoleBytes too big: %lld. Reset to zero.\n",
499 vol.VolCatHoleBytes);
500 vol.VolCatHoleBytes = 0;
501 }
502 /* Set device type where this Volume used */
503 if (vol.VolCatType == 0) {
504 vol.VolCatType = dev->dev_type;
505 }
506
507 /* Do not lock device here because it may be locked from label */
508 if (!jcr->is_canceled()) {
509 dir->fsend(Update_media, jcr->JobId,
510 VolumeName.c_str(), vol.VolCatJobs, vol.VolCatFiles,
511 vol.VolCatBlocks, edit_uint64(vol.VolCatAmetaBytes, ed1),
512 edit_uint64(vol.VolCatAdataBytes, ed2),
513 edit_uint64(vol.VolCatHoleBytes, ed3),
514 vol.VolCatHoles, vol.VolCatMounts, vol.VolCatErrors,
515 vol.VolCatWrites, edit_uint64(vol.VolCatMaxBytes, ed4),
516 edit_uint64(vol.VolLastWritten, ed5),
517 vol.VolCatStatus, vol.Slot, label,
518 InChanger, /* bool in structure */
519 edit_int64(vol.VolReadTime, ed6),
520 edit_int64(vol.VolWriteTime, ed7),
521 edit_uint64(vol.VolFirstWritten, ed8),
522 vol.VolCatType,
523 vol.VolCatParts,
524 vol.VolCatCloudParts,
525 vol.VolLastPartBytes,
526 Enabled,
527 Recycle);
528 Dmsg1(100, ">dird %s", dir->msg);
529 /*
530 * We sent info directly from dev to the Director.
531 * What the Director sends back is first read into
532 * the dcr with do_get_volume_info()
533 */
534 if (!do_get_volume_info(dcr)) {
535 Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
536 Dmsg2(dbglvl, _("Didn't get vol info vol=%s: ERR=%s"),
537 vol.VolCatName, jcr->errmsg);
538 goto bail_out;
539 }
540 Dmsg1(100, "get_volume_info() %s", dir->msg);
541
542 /* Update dev Volume info in case something changed (e.g. expired) */
543 if (!use_dcr_only) {
544 dev->VolCatInfo.Slot = dcr->VolCatInfo.Slot;
545 bstrncpy(dev->VolCatInfo.VolCatStatus, dcr->VolCatInfo.VolCatStatus, sizeof(vol.VolCatStatus));
546 dev->VolCatInfo.VolCatAdataBytes = dcr->VolCatInfo.VolCatAdataBytes;
547 dev->VolCatInfo.VolCatAmetaBytes = dcr->VolCatInfo.VolCatAmetaBytes;
548 dev->VolCatInfo.VolCatHoleBytes = dcr->VolCatInfo.VolCatHoleBytes;
549 dev->VolCatInfo.VolCatHoles = dcr->VolCatInfo.VolCatHoles;
550 dev->VolCatInfo.VolCatPadding = dcr->VolCatInfo.VolCatPadding;
551 dev->VolCatInfo.VolCatAmetaPadding = dcr->VolCatInfo.VolCatAmetaPadding;
552 dev->VolCatInfo.VolCatAdataPadding = dcr->VolCatInfo.VolCatAdataPadding;
553 dev->VolCatInfo.VolCatFiles = dcr->VolCatInfo.VolCatFiles;
554 dev->VolCatInfo.VolCatBytes = dcr->VolCatInfo.VolCatBytes;
555 dev->VolCatInfo.VolCatMounts = dcr->VolCatInfo.VolCatMounts;
556 dev->VolCatInfo.VolCatJobs = dcr->VolCatInfo.VolCatJobs;
557 dev->VolCatInfo.VolCatFiles = dcr->VolCatInfo.VolCatFiles;
558 dev->VolCatInfo.VolCatRecycles = dcr->VolCatInfo.VolCatRecycles;
559 dev->VolCatInfo.VolCatWrites = dcr->VolCatInfo.VolCatWrites;
560 dev->VolCatInfo.VolCatReads = dcr->VolCatInfo.VolCatReads;
561 dev->VolCatInfo.VolEnabled = dcr->VolCatInfo.VolEnabled;
562 dev->VolCatInfo.VolCatMaxBytes = dcr->VolCatInfo.VolCatMaxBytes;
563 dev->VolCatInfo.VolRecycle = dcr->VolCatInfo.VolRecycle;
564 dev->VolCatInfo.BytesWritten = 0;
565 }
566 ok = true;
567 }
568
569 bail_out:
570 dev->Unlock_VolCatInfo();
571 V(vol_info_mutex);
572 return ok;
573 }
574
575 struct JOBMEDIA_ITEM {
576 dlink link;
577 int64_t VolMediaId;
578 uint64_t StartAddr;
579 uint64_t EndAddr;
580 uint32_t VolFirstIndex;
581 uint32_t VolLastIndex;
582 uint32_t StartFile;
583 uint32_t EndFile;
584 uint32_t StartBlock;
585 uint32_t EndBlock;
586 };
587
create_jobmedia_queue(JCR * jcr)588 void create_jobmedia_queue(JCR *jcr)
589 {
590 JOBMEDIA_ITEM *item = NULL;
591 jcr->jobmedia_queue = New(dlist(item, &item->link));
592
593 FILEMEDIA_ITEM *fm = NULL;
594 jcr->filemedia_queue = New(dlist(fm, &fm->link));
595 }
596
597
flush_jobmedia_queue(JCR * jcr)598 bool flush_jobmedia_queue(JCR *jcr)
599 {
600 if (askdir_handler) {
601 return askdir_handler->flush_jobmedia_queue(jcr);
602 }
603
604 JOBMEDIA_ITEM *item;
605 BSOCK *dir = jcr->dir_bsock;
606 bool ok;
607
608 if (!flush_filemedia_queue(jcr)) {
609 return false; /* already in FATAL */
610 }
611
612 if (!jcr->jobmedia_queue || jcr->jobmedia_queue->size() == 0) {
613 return true; /* should never happen */
614 }
615 Dmsg1(400, "=== Flush jobmedia queue = %d\n", jcr->jobmedia_queue->size());
616
617 dir->fsend(Create_jobmedia, jcr->JobId);
618 foreach_dlist(item, jcr->jobmedia_queue) {
619 if (jcr->is_JobStatus(JS_Incomplete)) {
620 if (item->VolFirstIndex >= dir->get_lastFileIndex()) {
621 continue;
622 }
623 if (item->VolLastIndex >= dir->get_lastFileIndex()) {
624 item->VolLastIndex = dir->get_lastFileIndex() - 1;
625 }
626 }
627 ok = dir->fsend("%u %u %u %u %u %u %lld\n",
628 item->VolFirstIndex, item->VolLastIndex,
629 item->StartFile, item->EndFile,
630 item->StartBlock, item->EndBlock,
631 item->VolMediaId);
632 /* Keep track of last FileIndex flushed */
633 dir->set_lastFlushIndex(item->VolLastIndex);
634 Dmsg2(400, "sd->dir: ok=%d Jobmedia=%s", ok, dir->msg);
635 }
636 dir->signal(BNET_EOD);
637 jcr->jobmedia_queue->destroy();
638
639 if (dir->recv() <= 0) {
640 Dmsg0(dbglvl, "create_jobmedia error bnet_recv\n");
641 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia records: ERR=%s\n"),
642 dir->bstrerror());
643 return false;
644 }
645 Dmsg1(210, "<dird %s", dir->msg);
646 if (strcmp(dir->msg, OK_create) != 0) {
647 Dmsg1(dbglvl, "Bad response from Dir: %s\n", dir->msg);
648 Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia records: %s\n"), dir->msg);
649 return false;
650 }
651 return true;
652 }
653
654 /*
655 * After writing a Volume, create the JobMedia record.
656 */
dir_create_jobmedia_record(DCR * dcr,bool zero)657 bool dir_create_jobmedia_record(DCR *dcr, bool zero)
658 {
659 if (askdir_handler) {
660 return askdir_handler->dir_create_jobmedia_record(dcr, zero);
661 }
662
663 JCR *jcr = dcr->jcr;
664 BSOCK *dir = jcr->dir_bsock;
665 JOBMEDIA_ITEM *item;
666 bool ok = true;;
667
668 if (!zero && !dcr->WroteVol) {
669 return true;
670 }
671 if (!zero && dcr->VolLastIndex == 0) {
672 Pmsg7(0/*dbglvl*/, "Discard: JobMedia Vol=%s wrote=%d MediaId=%lld FI=%lu LI=%lu StartAddr=%lld EndAddr=%lld\n",
673 dcr->VolumeName, dcr->WroteVol, dcr->VolMediaId,
674 dcr->VolFirstIndex, dcr->VolLastIndex, dcr->StartAddr, dcr->EndAddr);
675 return true; /* nothing written to the Volume */
676 }
677 /* Throw out records where the start address is bigger than the end */
678 if (!zero && dcr->StartAddr > dcr->EndAddr) {
679 Pmsg7(0/*dbglvl*/, "Discard: JobMedia Vol=%s wrote=%d MediaId=%lld FI=%lu LI=%lu StartAddr=%lld EndAddr=%lld\n",
680 dcr->VolumeName, dcr->WroteVol, dcr->VolMediaId,
681 dcr->VolFirstIndex, dcr->VolLastIndex, dcr->StartAddr, dcr->EndAddr);
682 return true;
683 }
684
685 /* If system job, do not update catalog */
686 if (jcr->getJobType() == JT_SYSTEM) {
687 return true;
688 }
689
690 /* Throw out records where FI is zero -- i.e. nothing done */
691 if (!zero && dcr->VolFirstIndex == 0 &&
692 (dcr->StartAddr != 0 || dcr->EndAddr != 0)) {
693 Pmsg7(0/*dbglvl*/, "Discard: JobMedia Vol=%s wrote=%d MediaId=%lld FI=%lu LI=%lu StartAddr=%lld EndAddr=%lld\n",
694 dcr->VolumeName, dcr->WroteVol, dcr->VolMediaId,
695 dcr->VolFirstIndex, dcr->VolLastIndex, dcr->StartAddr, dcr->EndAddr);
696 return true;
697 }
698
699 /*
700 * If this Job is incomplete, we need to backup the FileIndex
701 * to the last correctly saved file so that the JobMedia
702 * LastIndex is correct.
703 *
704 */
705 if (jcr->is_JobStatus(JS_Incomplete)) {
706 dcr->VolLastIndex = dir->get_lastFileIndex();
707 Dmsg1(100, "======= Set FI=%ld\n", dcr->VolLastIndex);
708 }
709
710 Dmsg7(100, "Queue JobMedia Vol=%s wrote=%d MediaId=%lld FI=%lu LI=%lu StartAddr=%lld EndAddr=%lld\n",
711 dcr->VolumeName, dcr->WroteVol, dcr->VolMediaId,
712 dcr->VolFirstIndex, dcr->VolLastIndex, dcr->StartAddr, dcr->EndAddr);
713 item = (JOBMEDIA_ITEM *)malloc(sizeof(JOBMEDIA_ITEM));
714 if (zero) {
715 item->VolFirstIndex = item->VolLastIndex = 0;
716 item->StartFile = item->EndFile = 0;
717 item->StartBlock = item->EndBlock = 0;
718 item->StartAddr = item->EndAddr = 0;
719 item->VolMediaId = dcr->VolMediaId;
720 } else {
721 item->VolFirstIndex = dcr->VolFirstIndex;
722 item->VolLastIndex = dcr->VolLastIndex;
723 item->StartFile = (uint32_t)(dcr->StartAddr >> 32);
724 item->EndFile = (uint32_t)(dcr->EndAddr >> 32);
725 item->StartBlock = (uint32_t)dcr->StartAddr;
726 item->EndBlock = (uint32_t)dcr->EndAddr;
727 item->StartAddr = dcr->StartAddr;
728 item->EndAddr = dcr->EndAddr;
729 item->VolMediaId = dcr->VolMediaId;
730 }
731 jcr->jobmedia_queue->append(item);
732 /* Flush at queue size of 1000 jobmedia records */
733 if (zero || jcr->jobmedia_queue->size() >= 1000) {
734 ok = flush_jobmedia_queue(jcr);
735 }
736
737 dcr->VolFirstIndex = dcr->VolLastIndex = 0;
738 dcr->StartAddr = dcr->EndAddr = 0;
739 dcr->VolMediaId = 0;
740 dcr->WroteVol = false;
741 return ok;
742 }
743
744 /**
745 * Update File Attribute data
746 * We do the following:
747 * 1. expand the bsock buffer to be large enough
748 * 2. Write a "header" into the buffer with serialized data
749 * VolSessionId
750 * VolSeesionTime
751 * FileIndex
752 * Stream
753 * data length that follows
754 * start of raw byte data from the Device record.
755 * Note, this is primarily for Attribute data, but can
756 * also handle any device record. The Director must know
757 * the raw byte data format that is defined for each Stream.
758 * Now Restore Objects pass through here STREAM_RESTORE_OBJECT
759 */
dir_update_file_attributes(DCR * dcr,DEV_RECORD * rec)760 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
761 {
762 if (askdir_handler) {
763 return askdir_handler->dir_update_file_attributes(dcr, rec);
764 }
765
766 JCR *jcr = dcr->jcr;
767 BSOCK *dir = jcr->dir_bsock;
768 ser_declare;
769
770 #ifdef NO_ATTRIBUTES_TEST
771 return true;
772 #endif
773
774 dir->msg = check_pool_memory_size(dir->msg, sizeof(FileAttributes) +
775 MAX_NAME_LENGTH + sizeof(DEV_RECORD) + rec->data_len + 1);
776 dir->msglen = bsnprintf(dir->msg, sizeof(FileAttributes) +
777 MAX_NAME_LENGTH + 1, FileAttributes, jcr->JobId);
778 ser_begin(dir->msg + dir->msglen, 0);
779 ser_uint32(rec->VolSessionId);
780 ser_uint32(rec->VolSessionTime);
781 ser_int32(rec->FileIndex);
782 ser_int32(rec->Stream);
783 ser_uint32(rec->data_len);
784 ser_bytes(rec->data, rec->data_len);
785 dir->msglen = ser_length(dir->msg);
786 Dmsg1(1800, ">dird %s\n", dir->msg); /* Attributes */
787 if (rec->maskedStream == STREAM_UNIX_ATTRIBUTES ||
788 rec->maskedStream == STREAM_UNIX_ATTRIBUTES_EX) {
789 Dmsg2(1500, "==== set_data_end FI=%ld %s\n", rec->FileIndex, rec->data);
790 dir->set_data_end(rec->FileIndex); /* set offset of valid data */
791 }
792 return dir->send();
793 }
794
795
796 /**
797 * Request the sysop to create an appendable volume
798 *
799 * Entered with device blocked.
800 * Leaves with device blocked.
801 *
802 * Returns: true on success (operator issues a mount command)
803 * false on failure
804 * Note, must create dev->errmsg on error return.
805 *
806 * On success, dcr->VolumeName and dcr->VolCatInfo contain
807 * information on suggested volume, but this may not be the
808 * same as what is actually mounted.
809 *
810 * When we return with success, the correct tape may or may not
811 * actually be mounted. The calling routine must read it and
812 * verify the label.
813 */
dir_ask_sysop_to_create_appendable_volume(DCR * dcr)814 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
815 {
816 if (askdir_handler) {
817 return askdir_handler->dir_ask_sysop_to_create_appendable_volume(dcr);
818 }
819
820 int stat = W_TIMEOUT;
821 DEVICE *dev = dcr->dev;
822 JCR *jcr = dcr->jcr;
823 bool got_vol = false;
824
825 if (job_canceled(jcr)) {
826 dev->poll = false;
827 return false;
828 }
829 Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
830 ASSERT(dev->blocked());
831 for ( ;; ) {
832 if (job_canceled(jcr)) {
833 Mmsg(dev->errmsg,
834 _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
835 jcr->Job, dev->print_name());
836 Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
837 dev->poll = false;
838 return false;
839 }
840 got_vol = dir_find_next_appendable_volume(dcr); /* get suggested volume */
841 if (got_vol) {
842 goto get_out;
843 } else {
844 dev->clear_wait();
845 if (stat == W_TIMEOUT || stat == W_MOUNT) {
846 Mmsg(dev->errmsg, _(
847 "Job %s is waiting. Cannot find any appendable volumes.\n"
848 "Please use the \"label\" command to create a new Volume for:\n"
849 " Storage: %s\n"
850 " Pool: %s\n"
851 " Media type: %s\n"),
852 jcr->Job,
853 dev->print_name(),
854 dcr->pool_name,
855 dcr->media_type);
856 Jmsg(jcr, M_MOUNT, 0, "%s", dev->errmsg);
857 Dmsg1(dbglvl, "%s", dev->errmsg);
858 }
859 }
860
861 jcr->sendJobStatus(JS_WaitMedia);
862
863 stat = wait_for_sysop(dcr);
864 Dmsg1(dbglvl, "Back from wait_for_sysop stat=%d\n", stat);
865 if (dev->poll) {
866 Dmsg1(dbglvl, "Poll timeout in create append vol on device %s\n", dev->print_name());
867 continue;
868 }
869
870 if (stat == W_WAKE) {
871 /* Job could be marked to stopped, need to brea */
872 Mmsg0(dev->errmsg, _("Job was stopped by the user.\n"));
873 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
874 Dmsg1(dbglvl, "Job marked to be stopped. Gave up waiting on device %s\n", dev->print_name());
875 dev->poll = false;
876 return false;
877 }
878
879 if (stat == W_TIMEOUT) {
880 if (!double_dev_wait_time(dev)) {
881 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
882 dev->print_name(), jcr->Job);
883 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
884 Dmsg1(dbglvl, "Gave up waiting on device %s\n", dev->print_name());
885 dev->poll = false;
886 return false; /* exceeded maximum waits */
887 }
888 continue;
889 }
890 if (stat == W_ERROR) {
891 berrno be;
892 Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
893 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
894 dev->poll = false;
895 return false;
896 }
897 Dmsg1(dbglvl, "Someone woke me for device %s\n", dev->print_name());
898 }
899
900 get_out:
901 dev->poll = false;
902 jcr->sendJobStatus(JS_Running);
903 Dmsg0(dbglvl, "leave dir_ask_sysop_to_create_appendable_volume\n");
904 return true;
905 }
906
907 /**
908 * Request to mount specific Volume
909 *
910 * Entered with device blocked and dcr->VolumeName is desired
911 * volume.
912 * Leaves with device blocked.
913 *
914 * Returns: true on success (operator issues a mount command)
915 * false on failure
916 * Note, must create dev->errmsg on error return.
917 *
918 */
dir_ask_sysop_to_mount_volume(DCR * dcr,bool write_access)919 bool dir_ask_sysop_to_mount_volume(DCR *dcr, bool write_access)
920 {
921 if (askdir_handler) {
922 return askdir_handler->dir_ask_sysop_to_mount_volume(dcr, write_access);
923 }
924
925 int stat = W_TIMEOUT;
926 DEVICE *dev = dcr->dev;
927 JCR *jcr = dcr->jcr;
928
929 Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
930 if (!dcr->VolumeName[0]) {
931 Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
932 dev->poll = false;
933 return false;
934 }
935
936 if (dcr->no_mount_request) {
937 Mmsg(dev->errmsg, _("The current operation doesn't support mount request\n"));
938 dev->poll = false;
939 return false;
940 }
941
942 for ( ;; ) {
943 if (job_canceled(jcr)) {
944 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
945 jcr->Job, dev->print_name());
946 dev->poll = false;
947 return false;
948 }
949
950 /*
951 * If we are not polling, and the wait timeout or the
952 * user explicitly did a mount, send him the message.
953 * Otherwise skip it.
954 */
955 if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
956 const char *msg;
957 if (write_access) {
958 msg = _("%sPlease mount append Volume \"%s\" or label a new one for:\n"
959 " Job: %s\n"
960 " Storage: %s\n"
961 " Pool: %s\n"
962 " Media type: %s\n");
963 } else {
964 msg = _("%sPlease mount read Volume \"%s\" for:\n"
965 " Job: %s\n"
966 " Storage: %s\n"
967 " Pool: %s\n"
968 " Media type: %s\n");
969 }
970 Jmsg(jcr, M_MOUNT, 0, msg,
971 dev->is_nospace()?_("\n\nWARNING: device is full! Please add more disk space then ...\n\n"):"",
972 dcr->VolumeName,
973 jcr->Job,
974 dev->print_name(),
975 dcr->pool_name,
976 dcr->media_type);
977 Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
978 dcr->VolumeName, dev->print_name(), jcr->Job);
979 }
980
981 jcr->sendJobStatus(JS_WaitMount);
982
983 stat = wait_for_sysop(dcr); /* wait on device */
984 Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
985 if (dev->poll) {
986 Dmsg1(100, "Poll timeout in mount vol on device %s\n", dev->print_name());
987 Dmsg1(100, "Blocked=%s\n", dev->print_blocked());
988 goto get_out;
989 }
990
991 if (stat == W_TIMEOUT) {
992 if (!double_dev_wait_time(dev)) {
993 Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
994 dev->print_name(), jcr->Job);
995 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
996 Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
997 dev->poll = false;
998 return false; /* exceeded maximum waits */
999 }
1000 continue;
1001 }
1002 if (stat == W_ERROR) {
1003 berrno be;
1004 Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
1005 Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
1006 dev->poll = false;
1007 return false;
1008 }
1009 Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
1010 break;
1011 }
1012
1013 get_out:
1014 if (job_canceled(jcr)) {
1015 Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
1016 jcr->Job, dev->print_name());
1017 dev->poll = false;
1018 return false;
1019 }
1020
1021 jcr->sendJobStatus(JS_Running);
1022 Dmsg0(100, "leave dir_ask_sysop_to_mount_volume\n");
1023 return true;
1024 }
1025