]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/autochanger.c
97aee96e646e9b57908d069a626ede2043a7294e
[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  *  On entry: loaded == 0 -- nothing to do
197  *            loaded  < 0 -- check if anything to do
198  *            loaded  > 0 -- load slot == loaded
199  */
200 bool unload_autochanger(DCR *dcr, int loaded)
201 {
202    DEVICE *dev = dcr->dev;
203    JCR *jcr = dcr->jcr;
204    int slot;
205    uint32_t timeout = dcr->device->max_changer_wait;
206    bool ok = true;
207
208    if (loaded == 0) {
209       return true;
210    }
211
212    if (!dev->is_autochanger() || !dcr->device->changer_name ||
213        !dcr->device->changer_command) {
214       return false;
215    }
216
217    offline_or_rewind_dev(dev);
218    /* We are going to load a new tape, so close the device */
219    force_close_device(dev);
220
221    if (loaded < 0) {
222       loaded = get_autochanger_loaded_slot(dcr);
223    }
224    if (loaded > 0) {
225       POOLMEM *changer = get_pool_memory(PM_FNAME);
226       Jmsg(jcr, M_INFO, 0,
227            _("3307 Issuing autochanger \"unload slot %d, drive %d\" command.\n"),
228            loaded, dev->drive_index);
229       slot = dcr->VolCatInfo.Slot;
230       dcr->VolCatInfo.Slot = loaded;
231       changer = edit_device_codes(dcr, changer, 
232                    dcr->device->changer_command, "unload");
233       lock_changer(dcr);
234       int stat = run_program(changer, timeout, NULL);
235       unlock_changer(dcr);
236       dcr->VolCatInfo.Slot = slot;
237       if (stat != 0) {
238          berrno be;
239          be.set_errno(stat);
240          Jmsg(jcr, M_INFO, 0, _("3995 Bad autochanger \"unload slot %d, drive %d\": ERR=%s.\n"),
241                  slot, dev->drive_index, be.strerror());
242          ok = false;
243       } else {
244          dev->Slot = 0;            /* nothing loaded */
245       }
246       free_pool_memory(changer);
247    }
248    return ok;
249 }
250
251
252 /*
253  * List the Volumes that are in the autoloader possibly
254  *   with their barcodes.
255  *   We assume that it is always the Console that is calling us.
256  */
257 bool autochanger_cmd(DCR *dcr, BSOCK *dir, const char *cmd)  
258 {
259    DEVICE *dev = dcr->dev;
260    uint32_t timeout = dcr->device->max_changer_wait;
261    POOLMEM *changer;
262    BPIPE *bpipe;
263    int len = sizeof_pool_memory(dir->msg) - 1;
264    bool ok = false;
265    int stat;
266
267    if (!dev->is_autochanger() || !dcr->device->changer_name ||
268        !dcr->device->changer_command) {
269       bnet_fsend(dir, _("3993 Device %s not an autochanger device.\n"),
270          dev->print_name());
271       return false;
272    }
273
274    changer = get_pool_memory(PM_FNAME);
275    /* List command? */
276    if (strcmp(cmd, "list") == 0) {
277       unload_autochanger(dcr, -1);
278    }
279
280    /* Now issue the command */
281    changer = edit_device_codes(dcr, changer, 
282                  dcr->device->changer_command, cmd);
283    bnet_fsend(dir, _("3306 Issuing autochanger \"%s\" command.\n"), cmd);
284    lock_changer(dcr);
285    bpipe = open_bpipe(changer, timeout, "r");
286    if (!bpipe) {
287       unlock_changer(dcr);
288       bnet_fsend(dir, _("3996 Open bpipe failed.\n"));
289       goto bail_out;
290    }
291    if (strcmp(cmd, "list") == 0) {
292       /* Get output from changer */
293       while (fgets(dir->msg, len, bpipe->rfd)) {
294          dir->msglen = strlen(dir->msg);
295          Dmsg1(100, "<stored: %s\n", dir->msg);
296          bnet_send(dir);
297       }
298    } else {
299       /* For slots command, read a single line */
300       bstrncpy(dir->msg, "slots=", len);
301       fgets(dir->msg+6, len-6, bpipe->rfd);
302       dir->msglen = strlen(dir->msg);
303       Dmsg1(100, "<stored: %s", dir->msg);
304       bnet_send(dir);
305    }
306                  
307    stat = close_bpipe(bpipe);
308    unlock_changer(dcr);
309    if (stat != 0) {
310       berrno be;
311       be.set_errno(stat);
312       bnet_fsend(dir, _("Autochanger error: ERR=%s\n"), be.strerror());
313    }
314    bnet_sig(dir, BNET_EOD);
315    ok = true;
316
317 bail_out:
318    free_pool_memory(changer);
319    return true;
320 }
321
322
323 /*
324  * Edit codes into ChangerCommand
325  *  %% = %
326  *  %a = archive device name
327  *  %c = changer device name
328  *  %d = changer drive index
329  *  %f = Client's name
330  *  %j = Job name
331  *  %o = command
332  *  %s = Slot base 0
333  *  %S = Slot base 1
334  *  %v = Volume name
335  *
336  *
337  *  omsg = edited output message
338  *  imsg = input string containing edit codes (%x)
339  *  cmd = command string (load, unload, ...)
340  *
341  */
342 char *edit_device_codes(DCR *dcr, char *omsg, const char *imsg, const char *cmd)
343 {
344    const char *p;
345    const char *str;
346    char add[20];
347
348    *omsg = 0;
349    Dmsg1(1800, "edit_device_codes: %s\n", imsg);
350    for (p=imsg; *p; p++) {
351       if (*p == '%') {
352          switch (*++p) {
353          case '%':
354             str = "%";
355             break;
356          case 'a':
357             str = dcr->dev->archive_name();
358             break;
359          case 'c':
360             str = NPRT(dcr->device->changer_name);
361             break;
362          case 'd':
363             sprintf(add, "%d", dcr->dev->drive_index);
364             str = add;
365             break;
366          case 'o':
367             str = NPRT(cmd);
368             break;
369          case 's':
370             sprintf(add, "%d", dcr->VolCatInfo.Slot - 1);
371             str = add;
372             break;
373          case 'S':
374             sprintf(add, "%d", dcr->VolCatInfo.Slot);
375             str = add;
376             break;
377          case 'j':                    /* Job name */
378             str = dcr->jcr->Job;
379             break;
380          case 'v':
381             str = NPRT(dcr->VolumeName);
382             break;
383          case 'f':
384             str = NPRT(dcr->jcr->client_name);
385             break;
386
387          default:
388             add[0] = '%';
389             add[1] = *p;
390             add[2] = 0;
391             str = add;
392             break;
393          }
394       } else {
395          add[0] = *p;
396          add[1] = 0;
397          str = add;
398       }
399       Dmsg1(1900, "add_str %s\n", str);
400       pm_strcat(&omsg, (char *)str);
401       Dmsg1(1800, "omsg=%s\n", omsg);
402    }
403    Dmsg1(800, "omsg=%s\n", omsg);
404    return omsg;
405 }