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