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