]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/reserve.c
e48b9817f3f1aa85540d3ba0a0af4bb2168f2ccb
[bacula/bacula] / bacula / src / stored / reserve.c
1 /*
2  *   Drive reservation functions for Storage Daemon
3  *
4  *   Kern Sibbald, MM
5  *
6  *   Split from job.c and acquire.c June 2005
7  *
8  *   Version $Id$
9  *
10  */
11 /*
12    Copyright (C) 2000-2005 Kern Sibbald
13
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License
16    version 2 as ammended with additional clauses defined in the
17    file LICENSE in the main source directory.
18
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
22    the file LICENSE for additional details.
23
24  */
25
26 #include "bacula.h"
27 #include "stored.h"
28
29 /*
30  *   Use Device command from Director
31  *   He tells is what Device Name to use, the Media Type,
32  *      the Pool Name, and the Pool Type.
33  *
34  *    Ensure that the device exists and is opened, then store
35  *      the media and pool info in the JCR.  This class is used
36  *      only temporarily in this file.
37  */
38 class DIRSTORE {
39 public:
40    alist *device;
41    bool append;
42    char name[MAX_NAME_LENGTH];
43    char media_type[MAX_NAME_LENGTH];
44    char pool_name[MAX_NAME_LENGTH];
45    char pool_type[MAX_NAME_LENGTH];
46 };
47
48 /* Reserve context */
49 class RCTX {
50 public:
51    alist *errors;
52    JCR *jcr;
53    char *device_name;
54    DIRSTORE *store;
55    DEVRES   *device;
56    bool PreferMountedVols;
57 };
58
59 static dlist *vol_list = NULL;
60 static pthread_mutex_t vol_list_lock = PTHREAD_MUTEX_INITIALIZER;
61
62 /* Forward referenced functions */
63 static int can_reserve_drive(DCR *dcr, bool PerferMountedVols);
64 static int search_res_for_device(RCTX &rctx);
65 static int reserve_device(RCTX &rctx);
66 static bool reserve_device_for_read(DCR *dcr);
67 static bool reserve_device_for_append(DCR *dcr, bool PreferMountedVols);
68 static bool use_storage_cmd(JCR *jcr);
69 bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx);
70
71 /* Requests from the Director daemon */
72 static char use_storage[]  = "use storage=%127s media_type=%127s "
73    "pool_name=%127s pool_type=%127s append=%d copy=%d stripe=%d\n";
74 static char use_device[]  = "use device=%127s\n";
75
76 /* Responses sent to Director daemon */
77 static char OK_device[] = "3000 OK use device device=%s\n";
78 static char NO_device[] = "3924 Device \"%s\" not in SD Device resources.\n";
79 static char BAD_use[]   = "3913 Bad use command: %s\n";
80
81 bool use_cmd(JCR *jcr) 
82 {
83    /*
84     * Get the device, media, and pool information
85     */
86    if (!use_storage_cmd(jcr)) {
87       set_jcr_job_status(jcr, JS_ErrorTerminated);
88       memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
89       return false;
90    }
91    return true;
92 }
93
94 static int my_compare(void *item1, void *item2)
95 {
96    return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name);
97 }
98
99
100 /*
101  * Put a new Volume entry in the Volume list. This
102  *  effectively reserves the volume so that it will
103  *  not be mounted again.
104  *
105  *  Return: VOLRES entry on success
106  *          NULL if the Volume is already in the list
107  */
108 VOLRES *new_volume(const char *VolumeName, DEVICE *dev)
109 {
110    VOLRES *vol, *nvol;
111    vol = (VOLRES *)malloc(sizeof(VOLRES));
112    memset(vol, 0, sizeof(VOLRES));
113    vol->vol_name = bstrdup(VolumeName);
114    vol->dev = dev;
115    P(vol_list_lock);
116    nvol = (VOLRES *)vol_list->binary_insert(vol, my_compare);
117    V(vol_list_lock);
118    if (nvol != vol) {
119       free(vol->vol_name);
120       free(vol);
121       if (dev) {
122          nvol->dev = dev;
123       }
124       return NULL;
125    }
126    return vol;
127 }
128
129 /*
130  * Search for a Volume name in the Volume list.
131  *
132  *  Returns: VOLRES entry on success
133  *           NULL if the Volume is not in the list
134  */
135 VOLRES *find_volume(const char *VolumeName)
136 {
137    VOLRES vol, *fvol;
138    vol.vol_name = bstrdup(VolumeName);
139    P(vol_list_lock);
140    fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
141    V(vol_list_lock);
142    free(vol.vol_name);
143    return fvol;
144 }
145
146 /*  
147  * Free a Volume from the Volume list
148  *
149  *  Returns: true if the Volume found and removed from the list
150  *           false if the Volume is not in the list
151  */
152 bool free_volume(DEVICE *dev)
153 {
154    VOLRES vol, *fvol;
155
156    if (dev->VolHdr.VolumeName[0] == 0) {
157       return false;
158    }
159    vol.vol_name = bstrdup(dev->VolHdr.VolumeName);
160    P(vol_list_lock);
161    fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
162    if (fvol) {
163       vol_list->remove(fvol);
164       free(fvol->vol_name);
165       free(fvol);
166    }
167    V(vol_list_lock);
168    free(vol.vol_name);
169    dev->VolHdr.VolumeName[0] = 0;
170    return fvol != NULL;
171 }
172
173 /*
174  * List Volumes -- this should be moved to status.c
175  */
176 void list_volumes(BSOCK *user)  
177 {
178    VOLRES *vol;
179    for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
180       bnet_fsend(user, "%s\n", vol->vol_name);
181    }
182 }
183       
184 /* Create the Volume list */
185 void create_volume_list()
186 {
187    VOLRES *dummy;
188    if (vol_list == NULL) {
189       vol_list = New(dlist(dummy, &dummy->link));
190    }
191 }
192
193 /* Release all Volumes from the list */
194 void free_volume_list()
195 {
196    VOLRES *vol;
197    if (!vol_list) {
198       return;
199    }
200    for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
201       Dmsg1(000, "Unreleased Volume=%s\n", vol->vol_name);
202    }
203    delete vol_list;
204    vol_list = NULL;
205 }
206
207 bool is_volume_in_use(const char *VolumeName) 
208 {
209    VOLRES *vol = find_volume(VolumeName);
210    if (!vol) {
211       return false;                   /* vol not in list */
212    }
213    if (!vol->dev) {                   /* vol not attached to device */
214       return false;
215    }
216    if (!vol->dev->is_busy()) {
217       return false;
218    }
219    return true;
220 }
221
222
223 static bool use_storage_cmd(JCR *jcr)
224 {
225    POOL_MEM store_name, dev_name, media_type, pool_name, pool_type;
226    BSOCK *dir = jcr->dir_bsock;
227    int append;
228    bool ok;       
229    int Copy, Stripe;
230    DIRSTORE *store;
231    RCTX rctx;
232    rctx.jcr = jcr;
233 #ifdef implemented
234    char *error;
235 #endif
236
237    /*
238     * If there are multiple devices, the director sends us
239     *   use_device for each device that it wants to use.
240     */
241    Dmsg1(100, "<dird: %s", dir->msg);
242    jcr->dirstore = New(alist(10, not_owned_by_alist));
243    do {
244       ok = sscanf(dir->msg, use_storage, store_name.c_str(), 
245                   media_type.c_str(), pool_name.c_str(), 
246                   pool_type.c_str(), &append, &Copy, &Stripe) == 7;
247       if (!ok) {
248          break;
249       }
250       unbash_spaces(store_name);
251       unbash_spaces(media_type);
252       unbash_spaces(pool_name);
253       unbash_spaces(pool_type);
254       store = new DIRSTORE;
255       jcr->dirstore->append(store);
256       memset(store, 0, sizeof(DIRSTORE));
257       store->device = New(alist(10));
258       bstrncpy(store->name, store_name, sizeof(store->name));
259       bstrncpy(store->media_type, media_type, sizeof(store->media_type));
260       bstrncpy(store->pool_name, pool_name, sizeof(store->pool_name));
261       bstrncpy(store->pool_type, pool_type, sizeof(store->pool_type));
262       store->append = append;
263
264       /* Now get all devices */
265       while (bnet_recv(dir) >= 0) {
266          ok = sscanf(dir->msg, use_device, dev_name.c_str()) == 1;
267          if (!ok) {
268             break;
269          }
270          unbash_spaces(dev_name);
271          store->device->append(bstrdup(dev_name.c_str()));
272       }
273    }  while (ok && bnet_recv(dir) >= 0);
274
275 #ifdef DEVELOPER
276    /* This loop is debug code and can be removed */
277    /* ***FIXME**** remove after 1.38 release */
278    char *device_name;
279    foreach_alist(store, jcr->dirstore) {
280       Dmsg4(100, "Storage=%s media_type=%s pool=%s pool_type=%s\n", 
281          store->name, store->media_type, store->pool_name, 
282          store->pool_type);
283       foreach_alist(device_name, store->device) {
284          Dmsg1(100, "   Device=%s\n", device_name);
285       }
286    }
287 #endif
288
289    /*                    
290     * At this point, we have a list of all the Director's Storage
291     *  resources indicated for this Job, which include Pool, PoolType,
292     *  storage name, and Media type.     
293     * Then for each of the Storage resources, we have a list of
294     *  device names that were given.
295     *
296     * Wiffle through them and find one that can do the backup.
297     */
298    if (ok) {
299       /*
300        * Make up to two passes. The first with PreferMountedVols possibly
301        *   set to true.  In that case, we look only for an available 
302        *   drive with something mounted. If that fails, then we
303        *   do a second pass with PerferMountedVols set false.
304        */
305       rctx.PreferMountedVols = jcr->PreferMountedVols;
306       ok = find_suitable_device_for_job(jcr, rctx);
307       if (ok) {
308          goto done;
309       }
310       if (rctx.PreferMountedVols) {
311          rctx.PreferMountedVols = false;
312          ok = find_suitable_device_for_job(jcr, rctx);
313          if (ok) {
314             goto done;
315          }
316       }
317       if (verbose) {
318          unbash_spaces(dir->msg);
319          pm_strcpy(jcr->errmsg, dir->msg);
320          Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
321       }
322       Jmsg(jcr, M_FATAL, 0, _("\n"
323          "     Device \"%s\" with MediaType \"%s\" requested by DIR not found in SD Device resources.\n"),
324            dev_name.c_str(), media_type.c_str());
325       bnet_fsend(dir, NO_device, dev_name.c_str());
326 #ifdef implemented
327       for (error=(char*)rctx->errors.first(); error;
328            error=(char*)rctx->errors.next()) {
329          Jmsg(jcr, M_INFO, 0, "%s", error);
330       }
331 #endif
332       Dmsg1(100, ">dird: %s\n", dir->msg);
333    } else {
334       unbash_spaces(dir->msg);
335       pm_strcpy(jcr->errmsg, dir->msg);
336       if (verbose) {
337          Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
338       }
339       bnet_fsend(dir, BAD_use, jcr->errmsg);
340       Dmsg1(100, ">dird: %s\n", dir->msg);
341    }
342
343 done:
344    foreach_alist(store, jcr->dirstore) {
345       delete store->device;
346       delete store;
347    }
348    delete jcr->dirstore;
349 #ifdef implemented
350    for (error=(char*)rctx->errors.first(); error;
351         error=(char*)rctx->errors.next()) {
352       free(error);
353    }
354 #endif
355    return ok;
356 }
357
358
359 /*
360  * Search for a device suitable for this job.
361  */
362 bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx)
363 {
364    bool first = true;
365    bool ok = false;
366    DCR *dcr = NULL;
367    DIRSTORE *store;
368    char *device_name;
369
370    init_jcr_device_wait_timers(jcr);
371    for ( ;; ) {
372       int need_wait = false;
373       foreach_alist(store, jcr->dirstore) {
374          rctx.store = store;
375          foreach_alist(device_name, store->device) {
376             int stat;
377             rctx.device_name = device_name;
378             stat = search_res_for_device(rctx); 
379             if (stat == 1) {             /* found available device */
380                dcr = jcr->dcr;
381                ok = true;
382                break;
383             } else if (stat == 0) {      /* device busy */
384                need_wait = true;
385             }
386             /* otherwise error */
387 //             rctx->errors.push(bstrdup(jcr->errmsg));
388          }
389       }
390       /*
391        * If there is some device for which we can wait, then
392        *  wait and try again until the wait time expires
393        */
394       if (!need_wait || !wait_for_device(jcr, first)) {
395          break;
396       }
397       first = false;
398 #ifdef implemented
399       for (error=(char*)rctx->errors.first(); error;
400            error=(char*)rctx->errors.next()) {
401          free(error);
402       }
403 #endif
404    }
405    if (!ok && dcr) {
406       free_dcr(dcr);
407    }
408
409    return ok;
410 }
411
412 /*
413  * Search for a particular storage device with particular storage
414  *  characteristics (MediaType).
415  */
416 static int search_res_for_device(RCTX &rctx) 
417 {
418    AUTOCHANGER *changer;
419    BSOCK *dir = rctx.jcr->dir_bsock;
420    bool ok;
421    int stat;
422
423    Dmsg1(100, "Search res for %s\n", rctx.device_name);
424    foreach_res(rctx.device, R_DEVICE) {
425       Dmsg1(100, "Try res=%s\n", rctx.device->hdr.name);
426       /* Find resource, and make sure we were able to open it */
427       if (fnmatch(rctx.device_name, rctx.device->hdr.name, 0) == 0 &&
428           strcmp(rctx.device->media_type, rctx.store->media_type) == 0) {
429          stat = reserve_device(rctx);
430          if (stat != 1) {
431             return stat;
432          }
433          Dmsg1(220, "Got: %s", dir->msg);
434          bash_spaces(rctx.device_name);
435          ok = bnet_fsend(dir, OK_device, rctx.device_name);
436          Dmsg1(100, ">dird: %s\n", dir->msg);
437          return ok ? 1 : -1;
438       }
439    }
440    foreach_res(changer, R_AUTOCHANGER) {
441       Dmsg1(100, "Try changer res=%s\n", changer->hdr.name);
442       /* Find resource, and make sure we were able to open it */
443       if (fnmatch(rctx.device_name, changer->hdr.name, 0) == 0) {
444          /* Try each device in this AutoChanger */
445          foreach_alist(rctx.device, changer->device) {
446             Dmsg1(100, "Try changer device %s\n", rctx.device->hdr.name);
447             stat = reserve_device(rctx);
448             if (stat == -1) {            /* hard error */
449                return -1;
450             }
451             if (stat == 0) {             /* must wait, try next one */
452                continue;
453             }
454             POOL_MEM dev_name;
455             Dmsg1(100, "Device %s opened.\n", rctx.device_name);
456             pm_strcpy(dev_name, rctx.device->hdr.name);
457             bash_spaces(dev_name);
458             ok = bnet_fsend(dir, OK_device, dev_name.c_str());  /* Return real device name */
459             Dmsg1(100, ">dird: %s\n", dir->msg);
460             return ok ? 1 : -1;
461          }
462       }
463    }
464    return 0;                    /* nothing found */
465 }
466
467 /*
468  *  Try to reserve a specific device.
469  *
470  *  Returns: 1 -- OK, have DCR
471  *           0 -- must wait
472  *          -1 -- fatal error
473  */
474 static int reserve_device(RCTX &rctx)
475 {
476    bool ok;
477    DCR *dcr;
478    const int name_len = MAX_NAME_LENGTH;
479    if (!rctx.device->dev) {
480       rctx.device->dev = init_dev(rctx.jcr, rctx.device);
481    }
482    if (!rctx.device->dev) {
483       if (rctx.device->changer_res) {
484         Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
485            "     Device \"%s\" in changer \"%s\" requested by DIR could not be opened or does not exist.\n"),
486              rctx.device->hdr.name, rctx.device_name);
487       } else {
488          Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
489             "     Device \"%s\" requested by DIR could not be opened or does not exist.\n"),
490               rctx.device_name);
491       }
492       return -1;  /* no use waiting */
493    }  
494    Dmsg1(100, "Found device %s\n", rctx.device->hdr.name);
495    dcr = new_dcr(rctx.jcr, rctx.device->dev);
496    if (!dcr) {
497       BSOCK *dir = rctx.jcr->dir_bsock;
498       bnet_fsend(dir, _("3926 Could not get dcr for device: %s\n"), rctx.device_name);
499       Dmsg1(100, ">dird: %s\n", dir->msg);
500       return -1;
501    }
502    rctx.jcr->dcr = dcr;
503    bstrncpy(dcr->pool_name, rctx.store->pool_name, name_len);
504    bstrncpy(dcr->pool_type, rctx.store->pool_type, name_len);
505    bstrncpy(dcr->media_type, rctx.store->media_type, name_len);
506    bstrncpy(dcr->dev_name, rctx.device_name, name_len);
507    if (rctx.store->append == SD_APPEND) {
508       ok = reserve_device_for_append(dcr, rctx.PreferMountedVols);
509       Dmsg3(200, "dev_name=%s mediatype=%s ok=%d\n", dcr->dev_name, dcr->media_type, ok);
510    } else {
511       ok = reserve_device_for_read(dcr);
512    }
513    if (!ok) {
514       free_dcr(rctx.jcr->dcr);
515       return 0;
516    }
517    return 1;
518 }
519
520 /*
521  * We "reserve" the drive by setting the ST_READ bit. No one else
522  *  should touch the drive until that is cleared.
523  *  This allows the DIR to "reserve" the device before actually
524  *  starting the job. 
525  */
526 static bool reserve_device_for_read(DCR *dcr)
527 {
528    DEVICE *dev = dcr->dev;
529    JCR *jcr = dcr->jcr;
530    bool ok = false;
531
532    ASSERT(dcr);
533
534    dev->block(BST_DOING_ACQUIRE);
535
536    if (device_is_unmounted(dev)) {             
537       Dmsg1(200, "Device %s is BLOCKED due to user unmount.\n", dev->print_name());
538       Mmsg(jcr->errmsg, _("Device %s is BLOCKED due to user unmount.\n"),
539            dev->print_name());
540       goto bail_out;
541    }
542
543    if (dev->is_busy()) {
544       Dmsg4(200, "Device %s is busy ST_READ=%d num_writers=%d reserved=%d.\n", dev->print_name(),
545          dev->state & ST_READ?1:0, dev->num_writers, dev->reserved_device);
546       Mmsg1(jcr->errmsg, _("Device %s is busy.\n"),
547             dev->print_name());
548       goto bail_out;
549    }
550
551    dev->clear_append();
552    dev->set_read();
553    ok = true;
554
555 bail_out:
556    dev->unblock();
557    return ok;
558 }
559
560
561 /*
562  * We reserve the device for appending by incrementing the 
563  *  reserved_device. We do virtually all the same work that
564  *  is done in acquire_device_for_append(), but we do
565  *  not attempt to mount the device. This routine allows
566  *  the DIR to reserve multiple devices before *really* 
567  *  starting the job. It also permits the SD to refuse 
568  *  certain devices (not up, ...).
569  *
570  * Note, in reserving a device, if the device is for the
571  *  same pool and the same pool type, then it is acceptable.
572  *  The Media Type has already been checked. If we are
573  *  the first tor reserve the device, we put the pool
574  *  name and pool type in the device record.
575  */
576 static bool reserve_device_for_append(DCR *dcr, bool PreferMountedVols) 
577 {
578    JCR *jcr = dcr->jcr;
579    DEVICE *dev = dcr->dev;
580    bool ok = false;
581
582    ASSERT(dcr);
583
584    dev->block(BST_DOING_ACQUIRE);
585
586    if (dev->can_read()) {
587       Mmsg1(jcr->errmsg, _("Device %s is busy reading.\n"), dev->print_name());
588       Dmsg1(100, "%s", jcr->errmsg);
589       goto bail_out;
590    }
591
592    if (device_is_unmounted(dev)) {
593       Mmsg(jcr->errmsg, _("Device %s is BLOCKED due to user unmount.\n"), dev->print_name());
594       Dmsg1(100, "%s", jcr->errmsg);
595       goto bail_out;
596    }
597
598    Dmsg1(190, "reserve_append device is %s\n", dev->is_tape()?"tape":"disk");
599
600    if (can_reserve_drive(dcr, PreferMountedVols) != 1) {
601       Mmsg1(jcr->errmsg, _("Device %s is busy writing on another Volume.\n"), dev->print_name());
602       Dmsg1(100, "%s", jcr->errmsg);
603       goto bail_out;
604    }
605
606    dev->reserved_device++;
607    Dmsg1(200, "============= Inc reserve=%d\n", dev->reserved_device);
608    dcr->reserved_device = true;
609    ok = true;
610
611 bail_out:
612    dev->unblock();
613    return ok;
614 }
615
616 /*
617  * Returns: 1 if drive can be reserved
618  *          0 if we should wait
619  *         -1 on error
620  */
621 static int can_reserve_drive(DCR *dcr, bool PreferMountedVols) 
622 {
623    DEVICE *dev = dcr->dev;
624    JCR *jcr = dcr->jcr;
625
626    if (PreferMountedVols && !dev->VolHdr.VolumeName[0] &&
627        dev->is_tape() && !dev->is_autochanger()) {
628       return 0;                 /* No volume mounted */
629    }
630
631    /*
632     * Handle the case that the drive is not yet in append mode
633     */
634    if (!dev->can_append() && dev->num_writers == 0) {
635       /* Now check if there are any reservations on the drive */
636       if (dev->reserved_device) {           
637          /* Yes, now check if we want the same Pool and pool type */
638          if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
639              strcmp(dev->pool_type, dcr->pool_type) == 0) {
640             /* OK, compatible device */
641          } else {
642             /* Drive not suitable for us */
643             return 0;                 /* wait */
644          }
645       } else {
646          /* Device is available but not yet reserved, reserve it for us */
647          bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
648          bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
649       }
650       return 1;                       /* reserve drive */
651    }
652
653    /*
654     * Check if device in append mode with no writers (i.e. available)
655     */
656    if (dev->can_append() && dev->num_writers == 0) {
657       /* Device is available but not yet reserved, reserve it for us */
658       bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
659       bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
660       return 1;
661    }
662    /*
663     * Check if the device is in append mode with writers (i.e.
664     *  available if pool is the same).
665     */
666    if (dev->can_append() || dev->num_writers > 0) {
667       Dmsg0(190, "device already in append.\n");
668       /* Yes, now check if we want the same Pool and pool type */
669       if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
670           strcmp(dev->pool_type, dcr->pool_type) == 0) {
671          /* OK, compatible device */
672          return 1;
673       } else {
674          /* Drive not suitable for us */
675          Jmsg(jcr, M_WARNING, 0, _("Device %s is busy writing on another Volume.\n"), dev->print_name());
676          return 0;                    /* wait */
677       }
678    } else {
679       Pmsg0(000, "Logic error!!!! Should not get here.\n");
680       Jmsg0(jcr, M_FATAL, 0, _("Logic error!!!! Should not get here.\n"));
681       return -1;                      /* error, should not get here */
682    }
683
684    return 0;
685 }