]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
49505dd4f8d8f1bb0943373cb8bcf56d89c50249
[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-2005 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
31 /*
32  * Called here to do an autoload using the autochanger, if
33  *  configured, and if a Slot has been defined for this Volume.
34  *  On success this routine loads the indicated tape, but the
35  *  label is not read, so it must be verified.
36  *
37  *  Note if dir is not NULL, it is the console requesting the
38  *   autoload for labeling, so we respond directly to the
39  *   dir bsock.
40  *
41  *  Returns: 1 on success
42  *           0 on failure (no changer available)
43  *          -1 on error on autochanger
44  */
45 int autoload_device(DCR *dcr, int writing, BSOCK *dir)
46 {
47    JCR *jcr = dcr->jcr;
48    DEVICE *dev = dcr->dev;
49    int slot;
50    int drive = dev->drive_index;
51    int rtn_stat = -1;                 /* error status */
52    POOLMEM *changer;
53
54    slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
55    /*
56     * Handle autoloaders here.  If we cannot autoload it, we
57     *  will return 0 so that the sysop will be asked to load it.
58     */
59    if (writing && dev->is_autochanger() && slot <= 0) {
60       if (dir) {
61          return 0;                    /* For user, bail out right now */
62       }
63       if (dir_find_next_appendable_volume(dcr)) {
64          slot = dcr->VolCatInfo.InChanger ? dcr->VolCatInfo.Slot : 0;
65       } else {
66          slot = 0;
67       }
68    }
69    Dmsg1(400, "Want changer slot=%d\n", slot);
70
71    changer = get_pool_memory(PM_FNAME);
72    if (slot > 0 && dcr->device->changer_name && dcr->device->changer_command) {
73       uint32_t timeout = dcr->device->max_changer_wait;
74       int loaded, status;
75
76       loaded = get_autochanger_loaded_slot(dcr);
77
78       /* If tape we want is not loaded, load it. */
79       if (loaded != slot) {
80          if (!unload_autochanger(dcr, loaded)) {
81             goto bail_out;
82          }
83          /*
84           * Load the desired cassette
85           */
86          Dmsg1(400, "Doing changer load slot %d\n", slot);
87          Jmsg(jcr, M_INFO, 0,
88               _("3304 Issuing autochanger \"load slot %d, drive %d\" command.\n"),
89               slot, drive);
90          dcr->VolCatInfo.Slot = slot;    /* slot to be loaded */
91          changer = edit_device_codes(dcr, changer, 
92                       dcr->device->changer_command, "load");
93          status = run_program(changer, timeout, NULL);
94          if (status == 0) {
95             Jmsg(jcr, M_INFO, 0, _("3305 Autochanger \"load slot %d, drive %d\", status is OK.\n"),
96                     slot, drive);
97             dev->Slot = slot;         /* set currently loaded slot */
98          } else {
99            berrno be;
100            be.set_errno(status);
101             Jmsg(jcr, M_FATAL, 0, _("3992 Bad autochanger \"load slot %d, drive %d\": ERR=%s.\n"),
102                     slot, drive, be.strerror());
103             goto bail_out;
104          }
105          unlock_changer(dcr);
106          Dmsg2(400, "load slot %d status=%d\n", slot, status);
107       } else {
108          status = 0;                  /* we got what we want */
109          dev->Slot = slot;            /* set currently loaded slot */
110       }
111       Dmsg1(400, "After changer, status=%d\n", status);
112       if (status == 0) {              /* did we succeed? */
113          rtn_stat = 1;                /* tape loaded by changer */
114       }
115    } else {
116       rtn_stat = 0;                   /* no changer found */
117    }
118    free_pool_memory(changer);
119    return rtn_stat;
120
121 bail_out:
122    free_pool_memory(changer);
123    unlock_changer(dcr);
124    return -1;
125
126 }
127
128 /*
129  * Returns: -1 if error from changer command
130  *          slot otherwise
131  */
132 int get_autochanger_loaded_slot(DCR *dcr)
133 {
134    JCR *jcr = dcr->jcr;
135    POOLMEM *changer, *results;
136    int status, loaded;
137    uint32_t timeout = dcr->device->max_changer_wait;
138    int drive = dcr->dev->drive_index;
139
140    results = get_pool_memory(PM_MESSAGE);
141    changer = get_pool_memory(PM_FNAME);
142
143    lock_changer(dcr);
144
145    /* Find out what is loaded, zero means device is unloaded */
146    Jmsg(jcr, M_INFO, 0, _("3301 Issuing autochanger \"loaded drive %d\" command.\n"),
147         drive);
148    changer = edit_device_codes(dcr, changer, dcr->device->changer_command, "loaded");
149    *results = 0;
150    status = run_program(changer, timeout, results);
151    Dmsg3(50, "run_prog: %s stat=%d result=%s\n", changer, status, results);
152    if (status == 0) {
153       loaded = atoi(results);
154       if (loaded > 0) {
155          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result is Slot %d.\n"),
156               drive, loaded);
157          dcr->dev->Slot = loaded;
158       } else {
159          Jmsg(jcr, M_INFO, 0, _("3302 Autochanger \"loaded drive %d\", result: nothing loaded.\n"),
160               drive);
161          dcr->dev->Slot = 0;
162       }
163    } else {
164       berrno be;
165       be.set_errno(status);
166       Jmsg(jcr, M_INFO, 0, _("3991 Bad autochanger \"loaded drive %d\" command: ERR=%s.\n"),
167            drive, be.strerror());
168       loaded = -1;              /* force unload */
169    }
170    unlock_changer(dcr);
171    free_pool_memory(changer);
172    free_pool_memory(results);
173    return loaded;
174 }
175
176 static void lock_changer(DCR *dcr)
177 {
178    AUTOCHANGER *changer_res = dcr->device->changer_res;
179    if (changer_res) {
180       Dmsg1(100, "Locking changer %s\n", changer_res->hdr.name);
181       P(changer_res->changer_mutex);  /* Lock changer script */
182    }
183 }
184
185 static void unlock_changer(DCR *dcr)
186 {
187    AUTOCHANGER *changer_res = dcr->device->changer_res;
188    if (changer_res) {
189       Dmsg1(100, "Unlocking changer %s\n", changer_res->hdr.name);
190       V(changer_res->changer_mutex);  /* Unlock changer script */
191    }
192 }
193
194 /*
195  * Unload the volume, if any, in this drive
196  */
197 bool unload_autochanger(DCR *dcr, int loaded)
198 {
199    DEVICE *dev = dcr->dev;
200    JCR *jcr = dcr->jcr;
201    int slot;
202    uint32_t timeout = dcr->device->max_changer_wait;
203
204    if (!dev->is_autochanger() || !dcr->device->changer_name ||
205        !dcr->device->changer_command) {
206       return false;
207    }
208
209    offline_or_rewind_dev(dev);
210    /* We are going to load a new tape, so close the device */
211    force_close_device(dev);
212
213    if (loaded <= 0) {
214       loaded = get_autochanger_loaded_slot(dcr);
215    }
216    if (loaded > 0) {
217       POOLMEM *changer = get_pool_memory(PM_FNAME);
218       Jmsg(jcr, M_INFO, 0,
219            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
220            loaded, dev->drive_index);
221       slot = dcr->VolCatInfo.Slot;
222       dcr->VolCatInfo.Slot = loaded;
223       changer = edit_device_codes(dcr, changer, 
224                    dcr->device->changer_command, "unload");
225       lock_changer(dcr);
226       int stat = run_program(changer, timeout, NULL);
227       unlock_changer(dcr);
228       dcr->VolCatInfo.Slot = slot;
229       if (stat != 0) {
230          berrno be;
231          be.set_errno(stat);
232          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
233                  slot, dev->drive_index, be.strerror());
234          free_pool_memory(changer);
235          return false;
236       } else {
237          dev->Slot = 0;            /* nothing loaded */
238       }
239       free_pool_memory(changer);
240    }
241    return true;
242 }
243
244
245 /*
246  * List the Volumes that are in the autoloader possibly
247  *   with their barcodes.
248  *   We assume that it is always the Console that is calling us.
249  */
250 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
251 {
252    DEVICE *dev = dcr->dev;
253    uint32_t timeout = dcr->device->max_changer_wait;
254    POOLMEM *changer;
255    BPIPE *bpipe;
256    int len = sizeof_pool_memory(dir->msg) - 1;
257    bool ok = false;
258    int stat;
259
260    if (!dev->is_autochanger() || !dcr->device->changer_name ||
261        !dcr->device->changer_command) {
262       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
263          dev->print_name());
264       return false;
265    }
266
267    changer = get_pool_memory(PM_FNAME);
268    /* List command? */
269    if (strcmp(cmd, "list") == 0) {
270       unload_autochanger(dcr, 0);
271    }
272
273    /* Now issue the command */
274    changer = edit_device_codes(dcr, changer, 
275                  dcr->device->changer_command, cmd);
276    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
277    lock_changer(dcr);
278    bpipe = open_bpipe(changer, timeout, "r");
279    if (!bpipe) {
280       unlock_changer(dcr);
281       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
282       goto bail_out;
283    }
284    if (strcmp(cmd, "list") == 0) {
285       /* Get output from changer */
286       while (fgets(dir->msg, len, bpipe->rfd)) {
287          dir->msglen = strlen(dir->msg);
288          Dmsg1(100, "<stored: %s\n", dir->msg);
289          bnet_send(dir);
290       }
291    } else {
292       /* For slots command, read a single line */
293       bstrncpy(dir->msg, "slots=", len);
294       fgets(dir->msg+6, len-6, bpipe->rfd);
295       dir->msglen = strlen(dir->msg);
296       Dmsg1(100, "<stored: %s", dir->msg);
297       bnet_send(dir);
298    }
299                  
300    stat = close_bpipe(bpipe);
301    unlock_changer(dcr);
302    if (stat != 0) {
303       berrno be;
304       be.set_errno(stat);
305       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
306    }
307    bnet_sig(dir, BNET_EOD);
308    ok = true;
309
310 bail_out:
311    free_pool_memory(changer);
312    return true;
313 }
314
315
316 /*
317  * Edit codes into ChangerCommand
318  *  %% = %
319  *  %a = archive device name
320  *  %c = changer device name
321  *  %d = changer drive index
322  *  %f = Client's name
323  *  %j = Job name
324  *  %o = command
325  *  %s = Slot base 0
326  *  %S = Slot base 1
327  *  %v = Volume name
328  *
329  *
330  *  omsg = edited output message
331  *  imsg = input string containing edit codes (%x)
332  *  cmd = command string (load, unload, ...)
333  *
334  */
335 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
336 {
337    const char *p;
338    const char *str;
339    char add[20];
340
341    *omsg = 0;
342    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
343    for (p=imsg; *p; p++) {
344       if (*p == '%') {
345          switch (*++p) {
346          case '%':
347             str = "%";
348             break;
349          case 'a':
350             str = dcr->dev->archive_name();
351             break;
352          case 'c':
353             str = NPRT(dcr->device->changer_name);
354             break;
355          case 'd':
356             sprintf(add, "%d", dcr->dev->drive_index);
357             str = add;
358             break;
359          case 'o':
360             str = NPRT(cmd);
361             break;
362          case 's':
363             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
364             str = add;
365             break;
366          case 'S':
367             sprintf(add, "%d", dcr->VolCatInfo.Slot);
368             str = add;
369             break;
370          case 'j':                    /* Job name */
371             str = dcr->jcr->Job;
372             break;
373          case 'v':
374             str = NPRT(dcr->VolumeName);
375             break;
376          case 'f':
377             str = NPRT(dcr->jcr->client_name);
378             break;
379
380          default:
381             add[0] = '%';
382             add[1] = *p;
383             add[2] = 0;
384             str = add;
385             break;
386          }
387       } else {
388          add[0] = *p;
389          add[1] = 0;
390          str = add;
391       }
392       Dmsg1(1900, "add_str %s\n", str);
393       pm_strcat(&omsg, (char *)str);
394       Dmsg1(1800, "omsg=%s\n", omsg);
395    }
396    Dmsg1(800, "omsg=%s\n", omsg);
397    return omsg;
398 }