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