1 /*
2 BAREOS® - Backup Archiving REcovery Open Sourced
3
4 Copyright (C) 2002-2012 Free Software Foundation Europe e.V.
5 Copyright (C) 2011-2016 Planets Communications B.V.
6 Copyright (C) 2013-2020 Bareos GmbH & Co. KG
7
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version three of the GNU Affero General Public
10 License as published by the Free Software Foundation and included
11 in the file LICENSE.
12
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 Affero General Public License for more details.
17
18 You should have received a copy of the GNU Affero 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
21 02110-1301, USA.
22 */
23 /*
24 * Kern Sibbald, August MMII
25 */
26 /**
27 * @file
28 * Routines for handling the autochanger.
29 */
30
31 #include "include/bareos.h"
32 #include "stored/stored.h"
33 #include "stored/stored_globals.h"
34 #include "stored/autochanger.h"
35 #include "stored/device_control_record.h"
36 #include "stored/wait.h"
37 #include "lib/berrno.h"
38 #include "lib/bnet.h"
39 #include "lib/edit.h"
40 #include "lib/util.h"
41 #include "lib/parse_conf.h"
42 #include "lib/bsock.h"
43 #include "lib/berrno.h"
44
45 namespace storagedaemon {
46
47 /* Forward referenced functions */
48 static bool LockChanger(DeviceControlRecord* dcr);
49 static bool UnlockChanger(DeviceControlRecord* dcr);
50 static bool UnloadOtherDrive(DeviceControlRecord* dcr,
51 slot_number_t slot,
52 bool lock_set);
53 static char* transfer_edit_device_codes(DeviceControlRecord* dcr,
54 POOLMEM*& omsg,
55 const char* imsg,
56 const char* cmd,
57 slot_number_t src_slot,
58 slot_number_t dst_slot);
59
60 /**
61 * Init all the autochanger resources found
62 */
InitAutochangers()63 bool InitAutochangers()
64 {
65 bool OK = true;
66 AutochangerResource* changer;
67 drive_number_t logical_drive_number;
68
69 /*
70 * Ensure that the media_type for each device is the same
71 */
72 foreach_res (changer, R_AUTOCHANGER) {
73 DeviceResource* device_resource = nullptr;
74
75 logical_drive_number = 0;
76 foreach_alist (device_resource, changer->device_resources) {
77 /*
78 * If the device does not have a changer name or changer command
79 * defined, used the one from the Autochanger resource
80 */
81 if (!device_resource->changer_name && changer->changer_name) {
82 device_resource->changer_name = strdup(changer->changer_name);
83 }
84
85 if (!device_resource->changer_command && changer->changer_command) {
86 device_resource->changer_command = strdup(changer->changer_command);
87 }
88
89 if (!device_resource->changer_name) {
90 Jmsg(NULL, M_ERROR, 0,
91 _("No Changer Name given for device %s. Cannot continue.\n"),
92 device_resource->resource_name_);
93 OK = false;
94 }
95
96 if (!device_resource->changer_command) {
97 Jmsg(NULL, M_ERROR, 0,
98 _("No Changer Command given for device %s. Cannot continue.\n"),
99 device_resource->resource_name_);
100 OK = false;
101 }
102
103 /*
104 * Give the drive in the autochanger a logical drive number.
105 */
106 device_resource->drive = logical_drive_number++;
107 }
108 }
109
110 return OK;
111 }
112
113 /**
114 * Called here to do an autoload using the autochanger, if
115 * configured, and if a Slot has been defined for this Volume.
116 * On success this routine loads the indicated tape, but the
117 * label is not read, so it must be verified.
118 *
119 * Note if dir is not NULL, it is the console requesting the
120 * autoload for labeling, so we respond directly to the dir bsock.
121 *
122 * Returns: 1 on success
123 * 0 on failure (no changer available)
124 * -1 on error on autochanger
125 * -2 on error locking the autochanger
126 */
AutoloadDevice(DeviceControlRecord * dcr,int writing,BareosSocket * dir)127 int AutoloadDevice(DeviceControlRecord* dcr, int writing, BareosSocket* dir)
128 {
129 POOLMEM* changer;
130 int rtn_stat = -1; /* error status */
131 slot_number_t wanted_slot;
132 JobControlRecord* jcr = dcr->jcr;
133 drive_number_t drive;
134 Device* volatile dev = dcr->dev;
135
136 if (!dev->AttachedToAutochanger()) {
137 Dmsg1(100, "Device %s is not attached to an autochanger\n",
138 dev->print_name());
139 return 0;
140 }
141
142 /*
143 * An empty ChangerCommand => virtual disk autochanger
144 */
145 if (dcr->device_resource->changer_command
146 && dcr->device_resource->changer_command[0] == 0) {
147 Dmsg0(100, "ChangerCommand=0, virtual disk changer\n");
148 return 1; /* nothing to load */
149 }
150
151 drive = dev->drive_index;
152 wanted_slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
153 Dmsg3(100, "autoload: slot=%hd InChgr=%d Vol=%s\n", dcr->VolCatInfo.Slot,
154 dcr->VolCatInfo.InChanger, dcr->getVolCatName());
155
156 /*
157 * Handle autoloaders here. If we cannot autoload it, we will return 0 so
158 * that the sysop will be asked to load it.
159 */
160 if (writing && (!IsSlotNumberValid(wanted_slot))) {
161 if (dir) { return 0; /* For user, bail out right now */ }
162
163 /*
164 * ***FIXME*** this really should not be here
165 */
166 if (dcr->DirFindNextAppendableVolume()) {
167 wanted_slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
168 } else {
169 wanted_slot = 0;
170 }
171 }
172 Dmsg1(400, "Want changer slot=%hd\n", wanted_slot);
173
174 changer = GetPoolMemory(PM_FNAME);
175 if (!IsSlotNumberValid(wanted_slot)) {
176 /*
177 * Suppress info when polling
178 */
179 if (!dev->poll) {
180 Jmsg(
181 jcr, M_INFO, 0,
182 _("No slot defined in catalog (slot=%hd) for Volume \"%s\" on %s.\n"),
183 wanted_slot, dcr->getVolCatName(), dev->print_name());
184 Jmsg(jcr, M_INFO, 0,
185 _("Cartridge change or \"update slots\" may be required.\n"));
186 }
187 rtn_stat = 0;
188 } else if (!dcr->device_resource->changer_name) {
189 /*
190 * Suppress info when polling
191 */
192 if (!dev->poll) {
193 Jmsg(jcr, M_INFO, 0,
194 _("No \"Changer Device\" for %s. Manual load of Volume may be "
195 "required.\n"),
196 dev->print_name());
197 }
198 rtn_stat = 0;
199 } else if (!dcr->device_resource->changer_command) {
200 /*
201 * Suppress info when polling
202 */
203 if (!dev->poll) {
204 Jmsg(jcr, M_INFO, 0,
205 _("No \"Changer Command\" for %s. Manual load of Volume may be "
206 "required.\n"),
207 dev->print_name());
208 }
209 rtn_stat = 0;
210 } else {
211 uint32_t timeout = dcr->device_resource->max_changer_wait;
212 int status;
213 slot_number_t loaded_slot;
214
215 /*
216 * Attempt to load the Volume
217 */
218 loaded_slot = GetAutochangerLoadedSlot(dcr);
219 if (loaded_slot != wanted_slot) {
220 PoolMem results(PM_MESSAGE);
221
222 if (!LockChanger(dcr)) {
223 rtn_stat = -2;
224 goto bail_out;
225 }
226
227 /*
228 * Unload anything in our drive
229 */
230 if (!UnloadAutochanger(dcr, loaded_slot, true)) {
231 UnlockChanger(dcr);
232 goto bail_out;
233 }
234
235 /*
236 * Make sure desired slot is unloaded
237 */
238 if (!UnloadOtherDrive(dcr, wanted_slot, true)) {
239 UnlockChanger(dcr);
240 goto bail_out;
241 }
242
243 /*
244 * Load the desired volume.
245 */
246 Dmsg2(100, "Doing changer load slot %hd %s\n", wanted_slot,
247 dev->print_name());
248 Jmsg(
249 jcr, M_INFO, 0,
250 _("3304 Issuing autochanger \"load slot %hd, drive %hd\" command.\n"),
251 wanted_slot, drive);
252 dcr->VolCatInfo.Slot = wanted_slot; /* slot to be loaded */
253 changer = edit_device_codes(
254 dcr, changer, dcr->device_resource->changer_command, "load");
255 dev->close(dcr);
256 Dmsg1(200, "Run program=%s\n", changer);
257 status = RunProgramFullOutput(changer, timeout, results.addr());
258 if (status == 0) {
259 Jmsg(
260 jcr, M_INFO, 0,
261 _("3305 Autochanger \"load slot %hd, drive %hd\", status is OK.\n"),
262 wanted_slot, drive);
263 Dmsg2(100, "load slot %hd, drive %hd, status is OK.\n", wanted_slot,
264 drive);
265 dev->SetSlotNumber(wanted_slot); /* set currently loaded slot */
266 if (dev->vol) {
267 /*
268 * We just swapped this Volume so it cannot be swapping any more
269 */
270 dev->vol->ClearSwapping();
271 }
272 } else {
273 BErrNo be;
274 be.SetErrno(status);
275 std::string tmp(results.c_str());
276 if (tmp.find("Source Element Address") != std::string::npos
277 && tmp.find("is Empty") != std::string::npos) {
278 rtn_stat = -3; /* medium not found in slot */
279 } else {
280 rtn_stat = -1; /* hard error */
281 }
282 Dmsg3(100, "load slot %hd, drive %hd, bad stats=%s.\n", wanted_slot,
283 drive, be.bstrerror());
284 Jmsg(jcr, rtn_stat == -3 ? M_ERROR : M_FATAL, 0,
285 _("3992 Bad autochanger \"load slot %hd, drive %hd\": "
286 "ERR=%s.\nResults=%s\n"),
287 wanted_slot, drive, be.bstrerror(), results.c_str());
288 dev->SetSlotNumber(-1); /* mark unknown */
289 }
290 Dmsg2(100, "load slot %hd status=%d\n", wanted_slot, status);
291 UnlockChanger(dcr);
292 } else {
293 status = 0; /* we got what we want */
294 dev->SetSlotNumber(wanted_slot); /* set currently loaded slot */
295 }
296
297 Dmsg1(100, "After changer, status=%d\n", status);
298
299 if (status == 0) { /* did we succeed? */
300 rtn_stat = 1; /* tape loaded by changer */
301 }
302 }
303
304 bail_out:
305 FreePoolMemory(changer);
306 return rtn_stat;
307 }
308
309 /**
310 * Returns: -1 if error from changer command
311 * slot otherwise
312 *
313 * Note, this is safe to do without releasing the drive since it does not
314 * attempt load/unload a slot.
315 */
GetAutochangerLoadedSlot(DeviceControlRecord * dcr,bool lock_set)316 slot_number_t GetAutochangerLoadedSlot(DeviceControlRecord* dcr, bool lock_set)
317 {
318 int status;
319 POOLMEM* changer;
320 JobControlRecord* jcr = dcr->jcr;
321 slot_number_t loaded_slot = kInvalidSlotNumber;
322 Device* dev = dcr->dev;
323 PoolMem results(PM_MESSAGE);
324 uint32_t timeout = dcr->device_resource->max_changer_wait;
325 drive_number_t drive = dcr->dev->drive;
326
327 if (!dev->AttachedToAutochanger()) { return kInvalidSlotNumber; }
328
329 if (!dcr->device_resource->changer_command) { return kInvalidSlotNumber; }
330
331 slot_number_t slot = dev->GetSlot();
332 if (IsSlotNumberValid(slot)) { return slot; }
333
334 /*
335 * Virtual disk autochanger
336 */
337 if (dcr->device_resource->changer_command[0] == 0) { return 1; }
338
339 /*
340 * Only lock the changer if the lock_set is false e.g. changer not locked by
341 * calling function.
342 */
343 if (!lock_set) {
344 if (!LockChanger(dcr)) { return kInvalidSlotNumber; }
345 }
346
347 /*
348 * Find out what is loaded, zero means device is unloaded
349 * Suppress info when polling
350 */
351 if (!dev->poll && debug_level >= 1) {
352 Jmsg(jcr, M_INFO, 0,
353 _("3301 Issuing autochanger \"loaded? drive %hd\" command.\n"), drive);
354 }
355
356 changer = GetPoolMemory(PM_FNAME);
357 changer = edit_device_codes(dcr, changer,
358 dcr->device_resource->changer_command, "loaded");
359 Dmsg1(100, "Run program=%s\n", changer);
360 status = RunProgramFullOutput(changer, timeout, results.addr());
361 Dmsg3(100, "run_prog: %s stat=%d result=%s", changer, status,
362 results.c_str());
363
364 if (status == 0) {
365 loaded_slot = str_to_uint16(results.c_str());
366 if (IsSlotNumberValid(loaded_slot)) {
367 /*
368 * Suppress info when polling
369 */
370 if (!dev->poll && debug_level >= 1) {
371 Jmsg(jcr, M_INFO, 0,
372 _("3302 Autochanger \"loaded? drive %hd\", result is Slot %hd.\n"),
373 drive, loaded_slot);
374 }
375 dev->SetSlotNumber(loaded_slot);
376 } else {
377 /*
378 * Suppress info when polling
379 */
380 if (!dev->poll && debug_level >= 1) {
381 Jmsg(jcr, M_INFO, 0,
382 _("3302 Autochanger \"loaded? drive %hd\", result: nothing "
383 "loaded.\n"),
384 drive);
385 }
386 dev->SetSlotNumber(0);
387 }
388 } else {
389 BErrNo be;
390 be.SetErrno(status);
391 Jmsg(jcr, M_INFO, 0,
392 _("3991 Bad autochanger \"loaded? drive %hd\" command: "
393 "ERR=%s.\nResults=%s\n"),
394 drive, be.bstrerror(), results.c_str());
395 loaded_slot = kInvalidSlotNumber; /* force unload */
396 }
397
398 if (!lock_set) { UnlockChanger(dcr); }
399
400 FreePoolMemory(changer);
401
402 return loaded_slot;
403 }
404
LockChanger(DeviceControlRecord * dcr)405 static bool LockChanger(DeviceControlRecord* dcr)
406 {
407 AutochangerResource* changer_res = dcr->device_resource->changer_res;
408
409 if (changer_res) {
410 int errstat;
411 Dmsg1(200, "Locking changer %s\n", changer_res->resource_name_);
412 if ((errstat = RwlWritelock(&changer_res->changer_lock)) != 0) {
413 BErrNo be;
414 Jmsg(dcr->jcr, M_ERROR_TERM, 0,
415 _("Lock failure on autochanger. ERR=%s\n"), be.bstrerror(errstat));
416 }
417
418 /*
419 * We just locked the changer for exclusive use so let any plugin know we
420 * have.
421 */
422 if (GeneratePluginEvent(dcr->jcr, bSdEventChangerLock, dcr) != bRC_OK) {
423 Dmsg0(100, "Locking changer: bSdEventChangerLock failed\n");
424 RwlWriteunlock(&changer_res->changer_lock);
425 return false;
426 }
427 }
428
429 return true;
430 }
431
UnlockChanger(DeviceControlRecord * dcr)432 static bool UnlockChanger(DeviceControlRecord* dcr)
433 {
434 AutochangerResource* changer_res = dcr->device_resource->changer_res;
435
436 if (changer_res) {
437 int errstat;
438
439 GeneratePluginEvent(dcr->jcr, bSdEventChangerUnlock, dcr);
440
441 Dmsg1(200, "Unlocking changer %s\n", changer_res->resource_name_);
442 if ((errstat = RwlWriteunlock(&changer_res->changer_lock)) != 0) {
443 BErrNo be;
444 Jmsg(dcr->jcr, M_ERROR_TERM, 0,
445 _("Unlock failure on autochanger. ERR=%s\n"), be.bstrerror(errstat));
446 }
447 }
448
449 return true;
450 }
451
452 /**
453 * Unload the volume, if any, in this drive
454 * On entry: loaded_slot == 0 -- nothing to do
455 * loaded_slot == kInvalidSlotNumber -- check if anything to do
456 * loaded_slot > 0 -- load slot == loaded_slot
457 */
UnloadAutochanger(DeviceControlRecord * dcr,slot_number_t loaded_slot,bool lock_set)458 bool UnloadAutochanger(DeviceControlRecord* dcr,
459 slot_number_t loaded_slot,
460 bool lock_set)
461 {
462 Device* dev = dcr->dev;
463 JobControlRecord* jcr = dcr->jcr;
464 slot_number_t slot;
465 uint32_t timeout = dcr->device_resource->max_changer_wait;
466 bool retval = true;
467
468 if (loaded_slot == 0) { return true; }
469
470 if (!dev->AttachedToAutochanger() || !dcr->device_resource->changer_name
471 || !dcr->device_resource->changer_command) {
472 return false;
473 }
474
475 /*
476 * Virtual disk autochanger
477 */
478 if (dcr->device_resource->changer_command[0] == 0) {
479 dev->ClearUnload();
480 return true;
481 }
482
483 /*
484 * Only lock the changer if the lock_set is false e.g. changer not locked by
485 * calling function.
486 */
487 if (!lock_set) {
488 if (!LockChanger(dcr)) { return false; }
489 }
490
491 if (loaded_slot == kInvalidSlotNumber) {
492 loaded_slot = GetAutochangerLoadedSlot(dcr, true);
493 }
494
495 if (IsSlotNumberValid(loaded_slot)) {
496 int status;
497 PoolMem results(PM_MESSAGE);
498 POOLMEM* changer = GetPoolMemory(PM_FNAME);
499
500 Jmsg(
501 jcr, M_INFO, 0,
502 _("3307 Issuing autochanger \"unload slot %hd, drive %hd\" command.\n"),
503 loaded_slot, dev->drive);
504 slot = dcr->VolCatInfo.Slot;
505 dcr->VolCatInfo.Slot = loaded_slot;
506 changer = edit_device_codes(
507 dcr, changer, dcr->device_resource->changer_command, "unload");
508 dev->close(dcr);
509 Dmsg1(100, "Run program=%s\n", changer);
510 status = RunProgramFullOutput(changer, timeout, results.addr());
511 dcr->VolCatInfo.Slot = slot;
512 if (status != 0) {
513 BErrNo be;
514
515 be.SetErrno(status);
516 Jmsg(jcr, M_INFO, 0,
517 _("3995 Bad autochanger \"unload slot %hd, drive %hd\": "
518 "ERR=%s\nResults=%s\n"),
519 loaded_slot, dev->drive, be.bstrerror(), results.c_str());
520 retval = false;
521 dev->InvalidateSlotNumber(); /* unknown */
522 } else {
523 dev->SetSlotNumber(0); /* nothing loaded */
524 }
525
526 FreePoolMemory(changer);
527 }
528
529 /*
530 * Only unlock the changer if the lock_set is false e.g. changer not locked by
531 * calling function.
532 */
533 if (!lock_set) { UnlockChanger(dcr); }
534
535 // FreeVolume outside from changer lock
536 if (IsSlotNumberValid(loaded_slot)) {
537 FreeVolume(dev); /* Free any volume associated with this drive */
538 }
539
540 if (retval) { dev->ClearUnload(); }
541
542 return retval;
543 }
544
545 /**
546 * Unload the slot if mounted in a different drive
547 */
UnloadOtherDrive(DeviceControlRecord * dcr,slot_number_t slot,bool lock_set)548 static bool UnloadOtherDrive(DeviceControlRecord* dcr,
549 slot_number_t slot,
550 bool lock_set)
551 {
552 Device* dev = NULL;
553 Device* dev_save;
554 bool found = false;
555 AutochangerResource* changer = dcr->dev->device_resource->changer_res;
556 DeviceResource* device_resource = nullptr;
557 int retries = 0; /* wait for device retries */
558
559 if (!changer) { return false; }
560 if (changer->device_resources->size() == 1) { return true; }
561
562 /*
563 * We look for the slot number corresponding to the tape
564 * we want in other drives, and if possible, unload it.
565 */
566 Dmsg0(100, "Wiffle through devices looking for slot\n");
567 foreach_alist (device_resource, changer->device_resources) {
568 dev = device_resource->dev;
569 if (!dev) { continue; }
570 dev_save = dcr->dev;
571 dcr->SetDev(dev);
572
573 bool slotnumber_not_set = !IsSlotNumberValid(dev->GetSlot());
574 bool slot_not_loaded_in_autochanger
575 = !IsSlotNumberValid(GetAutochangerLoadedSlot(dcr, lock_set));
576
577 if (slotnumber_not_set && slot_not_loaded_in_autochanger) {
578 dcr->SetDev(dev_save);
579 continue;
580 }
581
582 dcr->SetDev(dev_save);
583 if (dev->GetSlot() == slot) {
584 found = true;
585 break;
586 }
587 }
588 if (!found) {
589 Dmsg1(100, "Slot=%hd not found in another device\n", slot);
590 return true;
591 } else {
592 Dmsg1(100, "Slot=%hd found in another device\n", slot);
593 }
594
595 /*
596 * The Volume we want is on another device.
597 */
598 if (dev->IsBusy()) {
599 Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%hd\n", dcr->VolumeName,
600 dcr->dev->print_name(), dev->print_name(), slot);
601 }
602
603 for (int i = 0; i < 3; i++) {
604 if (dev->IsBusy()) {
605 WaitForDevice(dcr->jcr, retries);
606 continue;
607 }
608 break;
609 }
610
611 if (dev->IsBusy()) {
612 Jmsg(dcr->jcr, M_WARNING, 0,
613 _("Volume \"%s\" wanted on %s is in use by device %s\n"),
614 dcr->VolumeName, dcr->dev->print_name(), dev->print_name());
615 Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%hd\n", dcr->VolumeName,
616 dcr->dev->print_name(), dev->print_name(), dev->GetSlot());
617 Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->NumReserved());
618 VolumeUnused(dcr);
619
620 return false;
621 }
622
623 return UnloadDev(dcr, dev, lock_set);
624 }
625
626 /**
627 * Unconditionally unload a specified drive
628 */
UnloadDev(DeviceControlRecord * dcr,Device * dev,bool lock_set)629 bool UnloadDev(DeviceControlRecord* dcr, Device* dev, bool lock_set)
630 {
631 int status;
632 Device* save_dev;
633 bool retval = true;
634 JobControlRecord* jcr = dcr->jcr;
635 slot_number_t save_slot;
636 uint32_t timeout = dcr->device_resource->max_changer_wait;
637 AutochangerResource* changer = dcr->dev->device_resource->changer_res;
638
639 if (!changer) { return false; }
640
641 save_dev = dcr->dev; /* save dcr device */
642 dcr->SetDev(dev); /* temporarily point dcr at other device */
643
644 /*
645 * Update slot if not set or not always_open
646 */
647 slot_number_t slot = dev->GetSlot();
648 if (!IsSlotNumberValid(slot) || !dev->HasCap(CAP_ALWAYSOPEN)) {
649 GetAutochangerLoadedSlot(dcr, lock_set);
650 }
651
652 /*
653 * Fail if we have no slot to unload
654 */
655 slot = dev->GetSlot();
656 if (!IsSlotNumberValid(slot)) {
657 dcr->SetDev(save_dev);
658 return false;
659 }
660
661 /*
662 * Only lock the changer if the lock_set is false e.g. changer not locked by
663 * calling function.
664 */
665 if (!lock_set) {
666 if (!LockChanger(dcr)) {
667 dcr->SetDev(save_dev);
668 return false;
669 }
670 }
671
672 save_slot = dcr->VolCatInfo.Slot;
673 dcr->VolCatInfo.Slot = dev->GetSlot();
674
675 POOLMEM* ChangerCmd = GetPoolMemory(PM_FNAME);
676 PoolMem results(PM_MESSAGE);
677
678 Jmsg(jcr, M_INFO, 0,
679 _("3307 Issuing autochanger \"unload slot %hd, drive %hd\" command.\n"),
680 dev->GetSlot(), dev->drive);
681
682 Dmsg2(100, "Issuing autochanger \"unload slot %hd, drive %hd\" command.\n",
683 dev->GetSlot(), dev->drive);
684
685 ChangerCmd = edit_device_codes(
686 dcr, ChangerCmd, dcr->device_resource->changer_command, "unload");
687 dev->close(dcr);
688
689 Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(),
690 dev->NumReserved());
691 Dmsg1(100, "Run program=%s\n", ChangerCmd);
692
693 status = RunProgramFullOutput(ChangerCmd, timeout, results.addr());
694 dcr->VolCatInfo.Slot = save_slot;
695 dcr->SetDev(save_dev);
696 if (status != 0) {
697 BErrNo be;
698 be.SetErrno(status);
699 Jmsg(jcr, M_INFO, 0,
700 _("3997 Bad autochanger \"unload slot %hd, drive %hd\": ERR=%s.\n"),
701 dev->GetSlot(), dev->drive, be.bstrerror());
702
703 Dmsg3(100, "Bad autochanger \"unload slot %hd, drive %hd\": ERR=%s.\n",
704 dev->GetSlot(), dev->drive, be.bstrerror());
705 retval = false;
706 dev->InvalidateSlotNumber(); /* unknown */
707 } else {
708 Dmsg2(100, "Slot %hd unloaded %s\n", dev->GetSlot(), dev->print_name());
709 dev->SetSlotNumber(0); /* nothing loaded */
710 }
711 if (retval) { dev->ClearUnload(); }
712
713 /*
714 * Only unlock the changer if the lock_set is false e.g. changer not locked by
715 * calling function.
716 */
717 if (!lock_set) { UnlockChanger(dcr); }
718
719 FreeVolume(dev); /* Free any volume associated with this drive */
720 FreePoolMemory(ChangerCmd);
721
722 return retval;
723 }
724
725 /**
726 * List the Volumes that are in the autoloader possibly with their barcodes.
727 * We assume that it is always the Console that is calling us.
728 */
AutochangerCmd(DeviceControlRecord * dcr,BareosSocket * dir,const char * cmd)729 bool AutochangerCmd(DeviceControlRecord* dcr,
730 BareosSocket* dir,
731 const char* cmd)
732 {
733 Device* dev = dcr->dev;
734 uint32_t timeout = dcr->device_resource->max_changer_wait;
735 POOLMEM* changer;
736 Bpipe* bpipe;
737 int len = SizeofPoolMemory(dir->msg) - 1;
738 int status;
739 int retries = 1; /* Number of retries on failing slot count */
740
741 if (!dev->AttachedToAutochanger() || !dcr->device_resource->changer_name
742 || !dcr->device_resource->changer_command) {
743 if (bstrcmp(cmd, "drives")) { dir->fsend("drives=1\n"); }
744 dir->fsend(_("3993 Device %s not an autochanger device.\n"),
745 dev->print_name());
746 return false;
747 }
748
749 if (bstrcmp(cmd, "drives")) {
750 AutochangerResource* changer_res = dcr->device_resource->changer_res;
751 int drives = 1;
752 if (changer_res) { drives = changer_res->device_resources->size(); }
753 dir->fsend("drives=%hd\n", drives);
754 Dmsg1(100, "drives=%hd\n", drives);
755 return true;
756 }
757
758 /*
759 * If listing, reprobe changer
760 */
761 if (bstrcmp(cmd, "list") || bstrcmp(cmd, "listall")) {
762 dcr->dev->SetSlotNumber(0);
763 GetAutochangerLoadedSlot(dcr);
764 }
765
766 changer = GetPoolMemory(PM_FNAME);
767 LockChanger(dcr);
768 changer = edit_device_codes(dcr, changer,
769 dcr->device_resource->changer_command, cmd);
770 dir->fsend(_("3306 Issuing autochanger \"%s\" command.\n"), cmd);
771
772 /*
773 * Now issue the command
774 */
775 retry_changercmd:
776 bpipe = OpenBpipe(changer, timeout, "r");
777 if (!bpipe) {
778 dir->fsend(_("3996 Open bpipe failed.\n"));
779 goto bail_out; /* TODO: check if we need to return false */
780 }
781
782 if (bstrcmp(cmd, "list") || bstrcmp(cmd, "listall")) {
783 /*
784 * Get output from changer
785 */
786 while (fgets(dir->msg, len, bpipe->rfd)) {
787 dir->message_length = strlen(dir->msg);
788 Dmsg1(100, "<stored: %s", dir->msg);
789 BnetSend(dir);
790 }
791 } else if (bstrcmp(cmd, "slots")) {
792 slot_number_t slots;
793 char buf[100], *p;
794
795 /*
796 * For slots command, read a single line
797 */
798 buf[0] = 0;
799 fgets(buf, sizeof(buf) - 1, bpipe->rfd);
800 buf[sizeof(buf) - 1] = 0;
801
802 /*
803 * Strip any leading space in front of # of slots
804 */
805 for (p = buf; B_ISSPACE(*p); p++) {}
806
807 /*
808 * Validate slot count. If slots == 0 retry retries more times.
809 */
810 slots = str_to_uint16(p);
811 if (slots == 0 && retries-- >= 0) {
812 CloseBpipe(bpipe);
813 goto retry_changercmd;
814 }
815
816 dir->fsend("slots=%hd", slots);
817 Dmsg1(100, "<stored: %s", dir->msg);
818 }
819
820 status = CloseBpipe(bpipe);
821 if (status != 0) {
822 BErrNo be;
823 be.SetErrno(status);
824 dir->fsend(_("3998 Autochanger error: ERR=%s\n"), be.bstrerror());
825 }
826
827 bail_out:
828 UnlockChanger(dcr);
829 FreePoolMemory(changer);
830 return true;
831 }
832
833 /**
834 * Transfer a volume from src_slot to dst_slot.
835 * We assume that it is always the Console that is calling us.
836 */
AutochangerTransferCmd(DeviceControlRecord * dcr,BareosSocket * dir,slot_number_t src_slot,slot_number_t dst_slot)837 bool AutochangerTransferCmd(DeviceControlRecord* dcr,
838 BareosSocket* dir,
839 slot_number_t src_slot,
840 slot_number_t dst_slot)
841 {
842 Device* dev = dcr->dev;
843 uint32_t timeout = dcr->device_resource->max_changer_wait;
844 POOLMEM* changer;
845 Bpipe* bpipe;
846 int len = SizeofPoolMemory(dir->msg) - 1;
847 int status;
848
849 if (!dev->AttachedToAutochanger() || !dcr->device_resource->changer_name
850 || !dcr->device_resource->changer_command) {
851 dir->fsend(_("3993 Device %s not an autochanger device.\n"),
852 dev->print_name());
853 return false;
854 }
855
856 changer = GetPoolMemory(PM_FNAME);
857 LockChanger(dcr);
858 changer = transfer_edit_device_codes(dcr, changer,
859 dcr->device_resource->changer_command,
860 "transfer", src_slot, dst_slot);
861 dir->fsend(_("3306 Issuing autochanger transfer command.\n"));
862
863 /*
864 * Now issue the command
865 */
866 bpipe = OpenBpipe(changer, timeout, "r");
867 if (!bpipe) {
868 dir->fsend(_("3996 Open bpipe failed.\n"));
869 goto bail_out; /* TODO: check if we need to return false */
870 }
871
872 /*
873 * Get output from changer
874 */
875 while (fgets(dir->msg, len, bpipe->rfd)) {
876 dir->message_length = strlen(dir->msg);
877 Dmsg1(100, "<stored: %s\n", dir->msg);
878 BnetSend(dir);
879 }
880
881 status = CloseBpipe(bpipe);
882 if (status != 0) {
883 BErrNo be;
884 be.SetErrno(status);
885 dir->fsend(_("3998 Autochanger error: ERR=%s\n"), be.bstrerror());
886 } else {
887 dir->fsend(
888 _("3308 Successfully transferred volume from slot %hd to %hd.\n"),
889 src_slot, dst_slot);
890 }
891
892 bail_out:
893 UnlockChanger(dcr);
894 FreePoolMemory(changer);
895 return true;
896 }
897
898 /**
899 * Special version of edit_device_codes for use with transfer subcommand.
900 * Edit codes into ChangerCommand
901 * %% = %
902 * %a = destination slot
903 * %c = changer device name
904 * %d = none
905 * %f = none
906 * %j = none
907 * %o = command
908 * %s = source slot
909 * %S = source slot
910 * %v = none
911 *
912 * omsg = edited output message
913 * imsg = input string containing edit codes (%x)
914 * cmd = command string (transfer)
915 */
transfer_edit_device_codes(DeviceControlRecord * dcr,POOLMEM * & omsg,const char * imsg,const char * cmd,slot_number_t src_slot,slot_number_t dst_slot)916 static char* transfer_edit_device_codes(DeviceControlRecord* dcr,
917 POOLMEM*& omsg,
918 const char* imsg,
919 const char* cmd,
920 slot_number_t src_slot,
921 slot_number_t dst_slot)
922 {
923 const char* p;
924 const char* str;
925 char ed1[50];
926
927 *omsg = 0;
928 Dmsg1(1800, "transfer_edit_device_codes: %s\n", imsg);
929 for (p = imsg; *p; p++) {
930 if (*p == '%') {
931 switch (*++p) {
932 case '%':
933 str = "%";
934 break;
935 case 'a':
936 str = edit_int64(dst_slot, ed1);
937 break;
938 case 'c':
939 str = NPRT(dcr->device_resource->changer_name);
940 break;
941 case 'o':
942 str = NPRT(cmd);
943 break;
944 case 's':
945 case 'S':
946 str = edit_int64(src_slot, ed1);
947 break;
948 default:
949 continue;
950 }
951 } else {
952 ed1[0] = *p;
953 ed1[1] = 0;
954 str = ed1;
955 }
956 Dmsg1(1900, "add_str %s\n", str);
957 PmStrcat(omsg, (char*)str);
958 Dmsg1(1800, "omsg=%s\n", omsg);
959 }
960 Dmsg1(800, "omsg=%s\n", omsg);
961
962 return omsg;
963 }
964
965 } /* namespace storagedaemon */
966