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