]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
Remove unwanted execute bit
[bacula/bacula] / bacula / src / stored / autochanger.c
1 /*
2  *
3  *  Routines for handling the autochanger.
4  *
5  *   Kern Sibbald, August MMII
6  *                            
7  *   Version $Id$
8  */
9 /*
10    Bacula® - The Network Backup Solution
11
12    Copyright (C) 2002-2006 Free Software Foundation Europe e.V.
13
14    The main author of Bacula is Kern Sibbald, with contributions from
15    many others, a complete list can be found in the file AUTHORS.
16    This program is Free Software; you can redistribute it and/or
17    modify it under the terms of version two of the GNU General Public
18    License as published by the Free Software Foundation plus additions
19    that are listed in the file LICENSE.
20
21    This program is distributed in the hope that it will be useful, but
22    WITHOUT ANY WARRANTY; without even the implied warranty of
23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24    General Public License for more details.
25
26    You should have received a copy of the GNU General Public License
27    along with this program; if not, write to the Free Software
28    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
29    02110-1301, USA.
30
31    Bacula® is a registered trademark of John Walker.
32    The licensor of Bacula is the Free Software Foundation Europe
33    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
34    Switzerland, email:ftf@fsfeurope.org.
35 */
36
37 #include "bacula.h"                   /* pull in global headers */
38 #include "stored.h"                   /* pull in Storage Deamon headers */
39
40 /* Forward referenced functions */
41 static void lock_changer(DCR *dcr);
42 static void unlock_changer(DCR *dcr);
43 static bool unload_other_drive(DCR *dcr, int slot);
44
45 /* Init all the autochanger resources found */
46 bool init_autochangers()
47 {
48    bool OK = true;
49    AUTOCHANGER *changer;
50    /* Ensure that the media_type for each device is the same */
51    foreach_res(changer, R_AUTOCHANGER) {
52       DEVRES *device;
53       foreach_alist(device, changer->device) {
54          /*
55           * If the device does not have a changer name or changer command
56           *   defined, used the one from the Autochanger resource 
57           */
58          if (!device->changer_name && changer->changer_name) {
59             device->changer_name = bstrdup(changer->changer_name);
60          }
61          if (!device->changer_command && changer->changer_command) {
62             device->changer_command = bstrdup(changer->changer_command);
63          }
64          if (!device->changer_name) {
65             Jmsg(NULL, M_ERROR, 0, 
66                _("No Changer Name given for device %s. Cannot continue.\n"),
67                device->hdr.name);
68             OK = false;
69          }   
70          if (!device->changer_command) {
71             Jmsg(NULL, M_ERROR, 0, 
72                _("No Changer Command given for device %s. Cannot continue.\n"),
73                device->hdr.name);
74             OK = false;
75          }   
76
77 #ifdef xxx_needed
78          if (media_type == NULL) {
79             media_type = device->media_type;     /* get Media Type of first device */
80             continue;
81          }     
82          /* Ensure that other devices Media Types are the same */
83          if (strcmp(media_type, device->media_type) != 0) {
84             Jmsg(NULL, M_ERROR, 0, 
85                _("Media Type not the same for all devices in changer %s. Cannot continue.\n"),
86                changer->hdr.name);
87             OK = false;
88             continue;
89          }
90 #endif
91       }
92    }
93    return OK;
94 }
95
96
97 /*
98  * Called here to do an autoload using the autochanger, if
99  *  configured, and if a Slot has been defined for this Volume.
100  *  On success this routine loads the indicated tape, but the
101  *  label is not read, so it must be verified.
102  *
103  *  Note if dir is not NULL, it is the console requesting the
104  *   autoload for labeling, so we respond directly to the
105  *   dir bsock.
106  *
107  *  Returns: 1 on success
108  *           0 on failure (no changer available)
109  *          -1 on error on autochanger
110  */
111 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
112 {
113    JCR *jcr = dcr->jcr;
114    DEVICE *dev = dcr->dev;
115    int slot;
116    int drive = dev->drive_index;
117    int rtn_stat = -1;                 /* error status */
118    POOLMEM *changer;
119
120    if (!dev->is_autochanger()) {
121       Dmsg1(200, "Device %s is not an autochanger\n", dev->print_name());
122       return 0;
123    }
124    slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
125    /*
126     * Handle autoloaders here.  If we cannot autoload it, we
127     *  will return 0 so that the sysop will be asked to load it.
128     */
129    if (writing && slot <= 0) {
130       if (dir) {
131          return 0;                    /* For user, bail out right now */
132       }
133       if (dir_find_next_appendable_volume(dcr)) {
134          slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
135       } else {
136          slot = 0;
137       }
138    }
139    Dmsg1(400, "Want changer slot=%d\n", slot);
140
141    changer = get_pool_memory(PM_FNAME);
142    if (slot <= 0) {
143       Jmsg(jcr, M_INFO, 0, _("Invalid slot=%d defined, cannot autoload Volume.\n"), slot);
144       rtn_stat = 0;
145    } else if (!dcr->device->changer_name) {
146       Jmsg(jcr, M_INFO, 0, _("No \"Changer Device\" given cannot autoload Volume.\n"));
147       rtn_stat = 0;
148   } else if (!dcr->device->changer_command) {
149       Jmsg(jcr, M_INFO, 0, _("No \"Changer Command\" given cannot autoload Volume.\n"));
150       rtn_stat = 0;
151   } else {
152       /* Attempt to load the Volume */
153
154       uint32_t timeout = dcr->device->max_changer_wait;
155       int loaded, status;
156
157       loaded = get_autochanger_loaded_slot(dcr);
158
159       if (loaded != slot) {
160          POOL_MEM results(PM_MESSAGE);
161
162          /* Unload anything in our drive */
163          if (!unload_autochanger(dcr, loaded)) {
164             goto bail_out;
165          }
166             
167          /* Make sure desired slot is unloaded */
168          if (!unload_other_drive(dcr, slot)) {
169             goto bail_out;
170          }
171
172          /*
173           * Load the desired cassette
174           */
175          lock_changer(dcr);
176          Dmsg1(100, "Doing changer load slot %d\n", slot);
177          Jmsg(jcr, M_INFO, 0,
178               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
179               slot, drive);
180          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
181          changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "load");
182          dev->close();
183          Dmsg1(200, "Run program=%s\n", changer);
184          status = run_program_full_output(changer, timeout, results.c_str());
185          if (status == 0) {
186             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
187                     slot, drive);
188             Dmsg2(100, "load slot %d, drive %d, status is OK.\n", slot, drive);
189             dev->Slot = slot;         /* set currently loaded slot */
190          } else {
191             berrno be;
192             be.set_errno(status);
193             Dmsg3(100, "load slot %d, drive %d, bad stats=%s.\n", slot, drive,
194                be.strerror());
195             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": "
196                  "ERR=%s.\nResults=%s\n"),
197                     slot, drive, be.strerror(), results.c_str());
198             rtn_stat = -1;            /* hard error */
199             dev->Slot = -1;           /* mark unknown */
200          }
201          Dmsg2(100, "load slot %d status=%d\n", slot, status);
202          unlock_changer(dcr);
203       } else {
204          status = 0;                  /* we got what we want */
205          dev->Slot = slot;            /* set currently loaded slot */
206       }
207       Dmsg1(100, "After changer, status=%d\n", status);
208       if (status == 0) {              /* did we succeed? */
209          rtn_stat = 1;                /* tape loaded by changer */
210       }
211    }
212    free_pool_memory(changer);
213    return rtn_stat;
214
215 bail_out:
216    free_pool_memory(changer);
217    return -1;
218
219 }
220
221 /*
222  * Returns: -1 if error from changer command
223  *          slot otherwise
224  *  Note, this is safe to do without releasing the drive
225  *   since it does not attempt load/unload a slot.
226  */
227 int get_autochanger_loaded_slot(DCR *dcr)
228 {
229    JCR *jcr = dcr->jcr;
230    DEVICE *dev = dcr->dev;
231    int status, loaded;
232    uint32_t timeout = dcr->device->max_changer_wait;
233    int drive = dcr->dev->drive_index;
234    POOL_MEM results(PM_MESSAGE);
235    POOLMEM *changer;
236
237    if (!dev->is_autochanger()) {
238       return -1;
239    }
240    if (!dcr->device->changer_command) {
241       Jmsg(jcr, M_FATAL, 0, _("3992 Missing Changer command.\n"));
242       return -1;
243    }
244    if (dev->Slot >0) {
245       return dev->Slot;
246    }
247
248    /* Find out what is loaded, zero means device is unloaded */
249    changer = get_pool_memory(PM_FNAME);
250    lock_changer(dcr);
251    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded? drive %d\" command.\n"),
252         drive);
253    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
254    *results.c_str() = 0;
255    Dmsg1(100, "Run program=%s\n", changer);
256    status = run_program_full_output(changer, timeout, results.c_str());
257    Dmsg3(100, "run_prog: %s stat=%d result=%s\n", changer, status, results.c_str());
258    if (status == 0) {
259       loaded = str_to_int32(results.c_str());
260       if (loaded > 0) {
261          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result is Slot %d.\n"),
262               drive, loaded);
263          dev->Slot = loaded;
264       } else {
265          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded? drive %d\", result: nothing loaded.\n"),
266               drive);
267          dev->Slot = -1;    /* unknown */
268       }
269    } else {
270       berrno be;
271       be.set_errno(status);
272       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded? drive %d\" command: "
273            "ERR=%s.\nResults=%s\n"), drive, be.strerror(), results.c_str());
274       loaded = -1;              /* force unload */
275    }
276    unlock_changer(dcr);
277    free_pool_memory(changer);
278    return loaded;
279 }
280
281 static void lock_changer(DCR *dcr)
282 {
283    AUTOCHANGER *changer_res = dcr->device->changer_res;
284    if (changer_res) {
285       Dmsg1(200, "Locking changer %s\n", changer_res->hdr.name);
286       P(changer_res->changer_mutex);  /* Lock changer script */
287    }
288 }
289
290 static void unlock_changer(DCR *dcr)
291 {
292    AUTOCHANGER *changer_res = dcr->device->changer_res;
293    if (changer_res) {
294       Dmsg1(200, "Unlocking changer %s\n", changer_res->hdr.name);
295       V(changer_res->changer_mutex);  /* Unlock changer script */
296    }
297 }
298
299 /*
300  * Unload the volume, if any, in this drive
301  *  On entry: loaded == 0 -- nothing to do
302  *            loaded  < 0 -- check if anything to do
303  *            loaded  > 0 -- load slot == loaded
304  */
305 bool unload_autochanger(DCR *dcr, int loaded)
306 {
307    DEVICE *dev = dcr->dev;
308    JCR *jcr = dcr->jcr;
309    int slot;
310    uint32_t timeout = dcr->device->max_changer_wait;
311    bool ok = true;
312
313    if (loaded == 0) {
314       return true;
315    }
316
317    if (!dev->is_autochanger() || !dcr->device->changer_name ||
318        !dcr->device->changer_command) {
319       return false;
320    }
321
322    if (loaded < 0) {
323       loaded = get_autochanger_loaded_slot(dcr);
324    }
325
326    if (loaded > 0) {
327       POOL_MEM results(PM_MESSAGE);
328       POOLMEM *changer = get_pool_memory(PM_FNAME);
329       lock_changer(dcr);
330       Jmsg(jcr, M_INFO, 0,
331            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
332            loaded, dev->drive_index);
333       slot = dcr->VolCatInfo.Slot;
334       dcr->VolCatInfo.Slot = loaded;
335       changer = edit_device_codes(dcr, changer, 
336                    dcr->device->changer_command, "unload");
337       dev->close();
338       Dmsg1(100, "Run program=%s\n", changer);
339       *results.c_str() = 0;
340       int stat = run_program_full_output(changer, timeout, results.c_str());
341       dcr->VolCatInfo.Slot = slot;
342       if (stat != 0) {
343          berrno be;
344          be.set_errno(stat);
345          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": "
346               "ERR=%s\nResults=%s\n"),
347                  loaded, dev->drive_index, be.strerror(), results.c_str());
348          ok = false;
349          dev->Slot = -1;           /* unknown */
350       } else {
351          dev->Slot = 0;            /* nothing loaded */
352       }
353       free_pool_memory(changer);
354       unlock_changer(dcr);
355    }
356    return ok;
357 }
358
359 /*
360  * Unload the slot if mounted in a different drive
361  */
362 static bool unload_other_drive(DCR *dcr, int slot)
363 {
364    DEVICE *dev = NULL;
365    DEVICE *save_dev;
366    JCR *jcr = dcr->jcr;
367    int save_slot;
368    uint32_t timeout = dcr->device->max_changer_wait;
369    bool ok = true;
370    AUTOCHANGER *changer = dcr->dev->device->changer_res;
371    DEVRES *device;
372    bool found = false;
373    bool first = true;
374
375    if (!changer) {
376       return false;
377    }
378    if (changer->device->size() == 1) {
379       return true;
380    }
381
382    foreach_alist(device, changer->device) {
383       if (device->dev && device->dev->Slot == slot) {
384          found = true;
385          dev = device->dev;
386          break;
387       }
388    }
389    if (!found) {
390       return true;
391    }
392
393    /* The Volume we want is on another device. */
394    if (dev->is_busy()) {
395       Dmsg4(100, "Vol %s for dev=%s in use dev=%s slot=%d\n",
396            dcr->VolumeName, dcr->dev->print_name(),
397            dev->print_name(), slot);
398    }   
399    for (int i=0; i < 3; i++) {
400       if (dev->is_busy()) {
401          wait_for_device(dcr->jcr, first);
402          first = false;
403          continue;
404       }
405       break;
406    }
407    P(dev->mutex);
408    if (dev->is_busy()) {
409       Jmsg(jcr, M_WARNING, 0, _("Volume \"%s\" is in use by device %s\n"),
410            dcr->VolumeName, dev->print_name());
411       Dmsg4(100, "Vol %s for dev=%s is busy dev=%s slot=%d\n",
412            dcr->VolumeName, dcr->dev->print_name(), dev->print_name(), slot);
413       Dmsg2(100, "num_writ=%d reserv=%d\n", dev->num_writers, dev->reserved_device);
414       V(dev->mutex);
415       return false;
416    }
417
418    POOLMEM *changer_cmd = get_pool_memory(PM_FNAME);
419    POOL_MEM results(PM_MESSAGE);
420    lock_changer(dcr);
421    Jmsg(jcr, M_INFO, 0,
422         _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
423         slot, dev->drive_index);
424
425    Dmsg2(100, "Issuing autochanger \"unload slot %d, drive %d\" command.\n",
426         slot, dev->drive_index);
427
428    save_dev = dcr->dev;
429    dcr->dev = dev;
430    save_slot = dcr->VolCatInfo.Slot;
431    dcr->VolCatInfo.Slot = slot;
432    changer_cmd = edit_device_codes(dcr, changer_cmd, 
433                 dcr->device->changer_command, "unload");
434    dev->close();
435    Dmsg2(200, "close dev=%s reserve=%d\n", dev->print_name(), 
436       dev->reserved_device);
437    Dmsg1(100, "Run program=%s\n", changer_cmd);
438    int stat = run_program_full_output(changer_cmd, timeout, results.c_str());
439    dcr->VolCatInfo.Slot = save_slot;
440    dcr->dev = save_dev;
441    if (stat != 0) {
442       berrno be;
443       be.set_errno(stat);
444       Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
445               slot, dev->drive_index, be.strerror());
446
447       Dmsg3(100, "Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n",
448               slot, dev->drive_index, be.strerror());
449       ok = false;
450       dev->Slot = -1;          /* unknown */
451    } else {
452       dev->Slot = 0;           /* nothing loaded */
453       Dmsg0(100, "Slot unloaded\n");
454    }
455    unlock_changer(dcr);
456    V(dev->mutex);
457    free_pool_memory(changer_cmd);
458    return ok;
459 }
460
461
462
463 /*
464  * List the Volumes that are in the autoloader possibly
465  *   with their barcodes.
466  *   We assume that it is always the Console that is calling us.
467  */
468 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
469 {
470    DEVICE *dev = dcr->dev;
471    uint32_t timeout = dcr->device->max_changer_wait;
472    POOLMEM *changer;
473    BPIPE *bpipe;
474    int len = sizeof_pool_memory(dir->msg) - 1;
475    bool ok = false;
476    int stat;
477
478    if (!dev->is_autochanger() || !dcr->device->changer_name ||
479        !dcr->device->changer_command) {
480       if (strcmp(cmd, "drives") == 0) {
481          bnet_fsend(dir, "drives=1\n");
482       }
483       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
484          dev->print_name());
485       return false;
486    }
487
488    if (strcmp(cmd, "drives") == 0) {
489       AUTOCHANGER *changer_res = dcr->device->changer_res;
490       int drives = 1;
491       if (changer_res) {
492          drives = changer_res->device->size();
493       }
494       bnet_fsend(dir, "drives=%d\n", drives);
495       Dmsg1(100, "drives=%d\n", drives);
496       return true;
497    }
498
499    changer = get_pool_memory(PM_FNAME);
500    lock_changer(dcr);
501    /* Now issue the command */
502    changer = edit_device_codes(dcr, changer, 
503                  dcr->device->changer_command, cmd);
504    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
505    bpipe = open_bpipe(changer, timeout, "r");
506    if (!bpipe) {
507       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
508       goto bail_out;
509    }
510    if (strcmp(cmd, "list") == 0) {
511       /* Get output from changer */
512       while (fgets(dir->msg, len, bpipe->rfd)) {
513          dir->msglen = strlen(dir->msg);
514          Dmsg1(100, "<stored: %s\n", dir->msg);
515          bnet_send(dir);
516       }
517    } else if (strcmp(cmd, "slots") == 0 ) {
518       char buf[100], *p;
519       /* For slots command, read a single line */
520       buf[0] = 0;
521       fgets(buf, sizeof(buf)-1, bpipe->rfd);
522       buf[sizeof(buf)-1] = 0;
523       /* Strip any leading space in front of # of slots */
524       for (p=buf; B_ISSPACE(*p); p++)
525         { }
526       bnet_fsend(dir, "slots=%s", p);
527       Dmsg1(100, "<stored: %s", dir->msg);
528    } 
529                  
530    stat = close_bpipe(bpipe);
531    if (stat != 0) {
532       berrno be;
533       be.set_errno(stat);
534       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
535    }
536    bnet_sig(dir, BNET_EOD);
537    ok = true;
538
539 bail_out:
540    unlock_changer(dcr);
541    free_pool_memory(changer);
542    return true;
543 }
544
545
546 /*
547  * Edit codes into ChangerCommand
548  *  %% = %
549  *  %a = archive device name
550  *  %c = changer device name
551  *  %d = changer drive index
552  *  %f = Client's name
553  *  %j = Job name
554  *  %o = command
555  *  %s = Slot base 0
556  *  %S = Slot base 1
557  *  %v = Volume name
558  *
559  *
560  *  omsg = edited output message
561  *  imsg = input string containing edit codes (%x)
562  *  cmd = command string (load, unload, ...)
563  *
564  */
565 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
566 {
567    const char *p;
568    const char *str;
569    char add[20];
570
571    *omsg = 0;
572    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
573    for (p=imsg; *p; p++) {
574       if (*p == '%') {
575          switch (*++p) {
576          case '%':
577             str = "%";
578             break;
579          case 'a':
580             str = dcr->dev->archive_name();
581             break;
582          case 'c':
583             str = NPRT(dcr->device->changer_name);
584             break;
585          case 'd':
586             sprintf(add, "%d", dcr->dev->drive_index);
587             str = add;
588             break;
589          case 'o':
590             str = NPRT(cmd);
591             break;
592          case 's':
593             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
594             str = add;
595             break;
596          case 'S':
597             sprintf(add, "%d", dcr->VolCatInfo.Slot);
598             str = add;
599             break;
600          case 'j':                    /* Job name */
601             str = dcr->jcr->Job;
602             break;
603          case 'v':
604             str = NPRT(dcr->VolumeName);
605             break;
606          case 'f':
607             str = NPRT(dcr->jcr->client_name);
608             break;
609
610          default:
611             add[0] = '%';
612             add[1] = *p;
613             add[2] = 0;
614             str = add;
615             break;
616          }
617       } else {
618          add[0] = *p;
619          add[1] = 0;
620          str = add;
621       }
622       Dmsg1(1900, "add_str %s\n", str);
623       pm_strcat(&omsg, (char *)str);
624       Dmsg1(1800, "omsg=%s\n", omsg);
625    }
626    Dmsg1(800, "omsg=%s\n", omsg);
627    return omsg;
628 }