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