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