]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/reserve.c
793f2a0b2b1e17b03acec284227c39656d5bd6f3
[bacula/bacula] / bacula / src / stored / reserve.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Drive reservation functions for Storage Daemon
30  *
31  *   Kern Sibbald, MM
32  *
33  *   Split from job.c and acquire.c June 2005
34  *
35  *   Version $Id$
36  *
37  */
38
39 #include "bacula.h"
40 #include "stored.h"
41
42 static dlist *vol_list = NULL;
43 static pthread_mutex_t vol_list_lock = PTHREAD_MUTEX_INITIALIZER;
44
45 /* Forward referenced functions */
46 static int can_reserve_drive(DCR *dcr, RCTX &rctx);
47 static int reserve_device(RCTX &rctx);
48 static bool reserve_device_for_read(DCR *dcr);
49 static bool reserve_device_for_append(DCR *dcr, RCTX &rctx);
50 static bool use_storage_cmd(JCR *jcr);
51 static void queue_reserve_message(JCR *jcr);
52
53 /* Requests from the Director daemon */
54 static char use_storage[]  = "use storage=%127s media_type=%127s "
55    "pool_name=%127s pool_type=%127s append=%d copy=%d stripe=%d\n";
56 static char use_device[]  = "use device=%127s\n";
57
58 /* Responses sent to Director daemon */
59 static char OK_device[] = "3000 OK use device device=%s\n";
60 static char NO_device[] = "3924 Device \"%s\" not in SD Device resources.\n";
61 static char BAD_use[]   = "3913 Bad use command: %s\n";
62
63 bool use_cmd(JCR *jcr) 
64 {
65    /*
66     * Get the device, media, and pool information
67     */
68    if (!use_storage_cmd(jcr)) {
69       set_jcr_job_status(jcr, JS_ErrorTerminated);
70       memset(jcr->sd_auth_key, 0, strlen(jcr->sd_auth_key));
71       return false;
72    }
73    return true;
74 }
75
76 static int my_compare(void *item1, void *item2)
77 {
78    return strcmp(((VOLRES *)item1)->vol_name, ((VOLRES *)item2)->vol_name);
79 }
80
81 static brwlock_t reservation_lock;
82
83 void init_reservations_lock()
84 {
85    int errstat;
86    if ((errstat=rwl_init(&reservation_lock)) != 0) {
87       berrno be;
88       Emsg1(M_ABORT, 0, _("Unable to initialize reservation lock. ERR=%s\n"),
89             be.strerror(errstat));
90    }
91
92 }
93
94 void term_reservations_lock()
95 {
96    rwl_destroy(&reservation_lock);
97 }
98
99 int reservations_lock_count = 0;
100
101 /* This applies to a drive and to Volumes */
102 void _lock_reservations()
103 {
104    int errstat;
105    reservations_lock_count++;
106    if ((errstat=rwl_writelock(&reservation_lock)) != 0) {
107       berrno be;
108       Emsg2(M_ABORT, 0, "rwl_writelock failure. stat=%d: ERR=%s\n",
109            errstat, be.strerror(errstat));
110    }
111 }
112
113 void _unlock_reservations()
114 {
115    int errstat;
116    reservations_lock_count--;
117    if ((errstat=rwl_writeunlock(&reservation_lock)) != 0) {
118       berrno be;
119       Emsg2(M_ABORT, 0, "rwl_writeunlock failure. stat=%d: ERR=%s\n",
120            errstat, be.strerror(errstat));
121    }
122 }
123
124 /*
125  * List Volumes -- this should be moved to status.c
126  */
127 enum {
128    debug_lock = true,
129    debug_nolock = false
130 };
131
132 static void debug_list_volumes(const char *imsg, bool do_lock)
133 {
134    VOLRES *vol;
135    POOL_MEM msg(PM_MESSAGE);
136    int count = 0;
137    DEVICE *dev = NULL;
138
139    if (do_lock) P(vol_list_lock);
140    for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
141       if (vol->dev) {
142          Mmsg(msg, "List from %s: %s at %p on device %s\n", imsg, 
143               vol->vol_name, vol->vol_name, vol->dev->print_name());
144       } else {
145          Mmsg(msg, "List from %s: %s at %p no dev\n", imsg, vol->vol_name, vol->vol_name);
146       }
147       Dmsg1(100, "%s", msg.c_str());
148       count++;
149    }
150
151    for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
152       if (vol->dev == dev) {
153          Dmsg0(000, "Two Volumes on same device.\n");
154          ASSERT(1);
155          dev = vol->dev;
156       }
157    }
158
159    Dmsg2(100, "List from %s: %d volumes\n", imsg, count);
160    if (do_lock) V(vol_list_lock);
161 }
162
163
164 /*
165  * List Volumes -- this should be moved to status.c
166  */
167 void list_volumes(void sendit(const char *msg, int len, void *sarg), void *arg)
168 {
169    VOLRES *vol;
170    POOL_MEM msg(PM_MESSAGE);
171    int len;
172
173    P(vol_list_lock);
174    for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
175       if (vol->dev) {
176          len = Mmsg(msg, "%s on device %s\n", vol->vol_name, vol->dev->print_name());
177          sendit(msg.c_str(), len, arg);
178       } else {
179          len = Mmsg(msg, "%s no dev\n", vol->vol_name);
180          sendit(msg.c_str(), len, arg);
181       }
182    }
183    V(vol_list_lock);
184 }
185
186 /*
187  * Create a Volume item to put in the Volume list
188  *   Ensure that the device points to it.
189  */
190 static VOLRES *new_vol_item(DCR *dcr, const char *VolumeName)
191 {
192    VOLRES *vol;
193    vol = (VOLRES *)malloc(sizeof(VOLRES));
194    memset(vol, 0, sizeof(VOLRES));
195    vol->vol_name = bstrdup(VolumeName);
196    vol->dev = dcr->dev;
197    Dmsg4(100, "New Vol=%s at %p dev=%s JobId=%u\n", VolumeName, vol->vol_name,
198          vol->dev->print_name(), (int)dcr->jcr->JobId);
199    return vol;
200 }
201
202 static void free_vol_item(VOLRES *vol)
203 {
204    free(vol->vol_name);
205    if (vol->dev) {
206       vol->dev->vol = NULL;
207    }
208    free(vol);
209 }
210
211
212 /*
213  * Put a new Volume entry in the Volume list. This
214  *  effectively reserves the volume so that it will
215  *  not be mounted again.
216  *
217  * If the device has any current volume associated with it,
218  *  and it is a different Volume, and the device is not busy,
219  *  we release the old Volume item and insert the new one.
220  * 
221  * It is assumed that the device is free and locked so that
222  *  we can change the device structure.
223  *
224  * Some details of the Volume list handling:
225  *
226  *  1. The Volume list entry must be attached to the drive (rather than 
227  *       attached to a job as it currently is. I.e. the drive that "owns" 
228  *       the volume (reserved, in use, mounted)
229  *       must point to the volume (still to be maintained in a list).
230  *
231  *  2. The Volume is entered in the list when a drive is reserved.  
232  *
233  *  3. When a drive is in use, the device code must appropriately update the
234  *      volume name as it changes (currently the list is static -- an entry is
235  *      removed when the Volume is no longer reserved, in use or mounted).  
236  *      The new code must keep the same list entry as long as the drive
237  *       has any volume associated with it but the volume name in the list
238  *       must be updated when the drive has a different volume mounted.
239  *
240  *  4. A job that has reserved a volume, can un-reserve the volume, and if the 
241  *      volume is not mounted, and not reserved, and not in use, it will be
242  *      removed from the list.
243  *
244  *  5. If a job wants to reserve a drive with a different Volume from the one on
245  *      the drive, it can re-use the drive for the new Volume.
246  *
247  *  6. If a job wants a Volume that is in a different drive, it can either use the
248  *      other drive or take the volume, only if the other drive is not in use or
249  *      not reserved.
250  *
251  *  One nice aspect of this is that the reserve use count and the writer use count 
252  *  already exist and are correctly programmed and will need no changes -- use 
253  *  counts are always very tricky.
254  *
255  *  The old code had a concept of "reserving" a Volume, but it needs to be changed 
256  *  to reserving and using a drive.  A volume is must be attached to (owned by) a 
257  *  drive and can move from drive to drive or be unused given certain specific 
258  *  conditions of the drive.  The key is that the drive must "own" the Volume.  
259  *  The old code has the job (dcr) owning the volume (more or less).  The job is 
260  *  to change the insertion and removal of the volumes from the list to be based 
261  *  on the drive rather than the job.  The new logic described above needs to be 
262  *  reviewed a couple more times for completeness and correctness.  Then I can 
263  *  program it.
264
265  *
266  *  Return: VOLRES entry on success
267  *          NULL error
268  */
269 VOLRES *reserve_volume(DCR *dcr, const char *VolumeName)
270 {
271    VOLRES *vol, *nvol;
272    DEVICE *dev = dcr->dev;
273
274    ASSERT(dev != NULL);
275
276    Dmsg1(100, "reserve_volume %s\n", VolumeName);
277    /* 
278     * We lock the reservations system here to ensure
279     *  when adding a new volume that no newly scheduled
280     *  job can reserve it.
281     */
282    lock_reservations();
283    P(vol_list_lock);
284    debug_list_volumes("begin reserve_volume", debug_nolock);
285    /* 
286     * First, remove any old volume attached to this device as it
287     *  is no longer used.
288     */
289    if (dev->vol) {
290       vol = dev->vol;
291       /*
292        * Make sure we don't remove the current volume we are inserting
293        *  because it was probably inserted by another job.
294        */
295       if (strcmp(vol->vol_name, VolumeName) == 0) {
296          goto get_out;                  /* Volume already on this device */
297       } else {
298          Dmsg3(100, "reserve_vol free vol=%s at %p JobId=%u\n", vol->vol_name,
299                vol->vol_name, (int)dcr->jcr->JobId);
300          debug_list_volumes("reserve_vol free", debug_nolock);
301          vol_list->remove(vol);
302          free_vol_item(vol);
303       }
304    }
305
306    /* Create a new Volume entry */
307    nvol = new_vol_item(dcr, VolumeName);
308
309    /*
310     * Now try to insert the new Volume
311     */
312    vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare);
313    if (vol != nvol) {
314       Dmsg2(100, "Found vol=%s dev-same=%d\n", vol->vol_name, dev==vol->dev);
315       /*
316        * At this point, a Volume with this name already is in the list,
317        *   so we simply release our new Volume entry. Note, this should
318        *   only happen if we are moving the volume from one drive to another.
319        */
320       Dmsg3(100, "reserve_vol free-tmp vol=%s at %p JobId=%u\n", vol->vol_name,
321             vol->vol_name, (int)dcr->jcr->JobId);
322       /*
323        * Clear dev pointer so that free_vol_item() doesn't 
324        *  take away our volume. 
325        */
326       nvol->dev = NULL;                   /* don't zap dev entry */
327       free_vol_item(nvol);
328
329       /* Check if we are trying to use the Volume on a different drive */
330       if (dev != vol->dev) {
331          /* Caller wants to switch Volume to another device */
332          if (!vol->dev->is_busy()) {
333             /* OK to move it -- I'm not sure this will work */
334             Dmsg3(100, "Swap vol=%s from dev=%s to %s\n", VolumeName,
335                dev->print_name(), dev->print_name());
336             vol->dev->vol = NULL;         /* take vol from old drive */
337             vol->dev->VolHdr.VolumeName[0] = 0;
338             vol->dev = dev;               /* point vol at new drive */
339             dev->vol = vol;               /* point dev at vol */
340             dev->VolHdr.VolumeName[0] = 0;
341          } else {
342             vol = NULL;                /* device busy */
343             Dmsg3(100, "Logic ERROR!!!! could not swap vol=%s from dev=%s to %s\n", VolumeName,
344                dev->print_name(), dcr->dev->print_name());
345             ASSERT(1);     /* blow up!!! */
346          }
347       }
348    }
349    dev->vol = vol;
350
351 get_out:
352    debug_list_volumes("end new volume", debug_nolock);
353    V(vol_list_lock);
354    unlock_reservations();
355    return vol;
356 }
357
358 /*
359  * Search for a Volume name in the Volume list.
360  *
361  *  Returns: VOLRES entry on success
362  *           NULL if the Volume is not in the list
363  */
364 VOLRES *find_volume(const char *VolumeName)
365 {
366    VOLRES vol, *fvol;
367    /* Do not lock reservations here */
368    P(vol_list_lock);
369    vol.vol_name = bstrdup(VolumeName);
370    fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
371    free(vol.vol_name);
372    Dmsg2(100, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL);
373    debug_list_volumes("find_volume", debug_nolock);
374    V(vol_list_lock);
375    return fvol;
376 }
377
378 /* 
379  * Remove any reservation from a drive and tell the system
380  *  that the volume is unused at least by us.
381  */
382 void unreserve_device(DCR *dcr)
383 {
384    DEVICE *dev = dcr->dev;
385    dev->dlock();
386    if (dcr->reserved_device) {
387       dcr->reserved_device = false;
388       dev->reserved_device--;
389       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
390       dcr->reserved_device = false;
391       /* If we set read mode in reserving, remove it */
392       if (dev->can_read()) {
393          dev->clear_read();
394       }
395       if (dev->num_writers < 0) {
396          Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers);
397          dev->num_writers = 0;
398       }
399    }
400
401    volume_unused(dcr);
402    dev->dunlock();
403 }
404
405 /*  
406  * Free a Volume from the Volume list if it is no longer used
407  *
408  *  Returns: true if the Volume found and removed from the list
409  *           false if the Volume is not in the list or is in use
410  */
411 bool volume_unused(DCR *dcr)
412 {
413    DEVICE *dev = dcr->dev;
414
415    if (dev->vol == NULL) {
416       Dmsg1(100, " unreserve_volume: no vol on %s\n", dev->print_name());
417       debug_list_volumes("null return unreserve_volume", debug_lock);
418       return false;
419    }
420
421    if (dev->is_busy()) {
422       Dmsg1(100, "unreserve_volume: dev is busy %s\n", dev->print_name());
423       debug_list_volumes("dev busy return unreserve_volume", debug_lock);
424       return false;
425    }
426
427    return free_volume(dev);
428 }
429
430 /*
431  * Unconditionally release the volume
432  */
433 bool free_volume(DEVICE *dev)
434 {
435    VOLRES *vol;
436
437    if (dev->vol == NULL) {
438       Dmsg1(100, "No vol on dev %s\n", dev->print_name());
439       return false;
440    }
441    P(vol_list_lock);
442    vol = dev->vol;
443    dev->vol = NULL;
444    Dmsg1(100, "free_volume %s\n", vol->vol_name);
445    vol_list->remove(vol);
446    Dmsg3(100, "free_volume %s at %p dev=%s\n", vol->vol_name, vol->vol_name,
447          dev->print_name());
448    free_vol_item(vol);
449    debug_list_volumes("free_volume", debug_nolock);
450    V(vol_list_lock);
451    return vol != NULL;
452 }
453
454       
455 /* Create the Volume list */
456 void create_volume_list()
457 {
458    VOLRES *dummy = NULL;
459    if (vol_list == NULL) {
460       vol_list = New(dlist(dummy, &dummy->link));
461    }
462 }
463
464 /* Release all Volumes from the list */
465 void free_volume_list()
466 {
467    VOLRES *vol;
468    if (!vol_list) {
469       return;
470    }
471    P(vol_list_lock);
472    for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
473       Dmsg2(100, "Unreleased Volume=%s dev=%p\n", vol->vol_name, vol->dev);
474       free(vol->vol_name);
475       vol->vol_name = NULL;
476    }
477    delete vol_list;
478    vol_list = NULL;
479    V(vol_list_lock);
480 }
481
482 bool is_volume_in_use(DCR *dcr)
483 {
484    VOLRES *vol = find_volume(dcr->VolumeName);
485    if (!vol) {
486       Dmsg1(100, "Vol=%s not in use.\n", dcr->VolumeName);
487       return false;                   /* vol not in list */
488    }
489    ASSERT(vol->dev != NULL);
490
491    if (dcr->dev == vol->dev) {        /* same device OK */
492       Dmsg1(100, "Vol=%s on same dev.\n", dcr->VolumeName);
493       return false;
494    }
495    if (!vol->dev->is_busy()) {
496       Dmsg2(100, "Vol=%s dev=%s not busy.\n", dcr->VolumeName, vol->dev->print_name());
497       return false;
498    }
499    Dmsg2(100, "Vol=%s used by %s.\n", dcr->VolumeName, vol->dev->print_name());
500    return true;
501 }
502
503
504 /*
505  * We get the following type of information:
506  *
507  * use storage=xxx media_type=yyy pool_name=xxx pool_type=yyy append=1 copy=0 strip=0
508  *  use device=zzz
509  *  use device=aaa
510  *  use device=bbb
511  * use storage=xxx media_type=yyy pool_name=xxx pool_type=yyy append=0 copy=0 strip=0
512  *  use device=bbb
513  *
514  */
515 static bool use_storage_cmd(JCR *jcr)
516 {
517    POOL_MEM store_name, dev_name, media_type, pool_name, pool_type;
518    BSOCK *dir = jcr->dir_bsock;
519    int append;
520    bool ok;       
521    int Copy, Stripe;
522    DIRSTORE *store;
523    RCTX rctx;
524    char *msg;
525    alist *msgs;
526    alist *dirstore;
527
528    memset(&rctx, 0, sizeof(RCTX));
529    rctx.jcr = jcr;
530    /*
531     * If there are multiple devices, the director sends us
532     *   use_device for each device that it wants to use.
533     */
534    dirstore = New(alist(10, not_owned_by_alist));
535 // Dmsg2(000, "dirstore=%p JobId=%u\n", dirstore, jcr->JobId);
536    msgs = jcr->reserve_msgs = New(alist(10, not_owned_by_alist));  
537    do {
538       Dmsg1(100, "<dird: %s", dir->msg);
539       ok = sscanf(dir->msg, use_storage, store_name.c_str(), 
540                   media_type.c_str(), pool_name.c_str(), 
541                   pool_type.c_str(), &append, &Copy, &Stripe) == 7;
542       if (!ok) {
543          break;
544       }
545       if (append) {
546          jcr->write_store = dirstore;
547       } else {
548          jcr->read_store = dirstore;
549       }
550       rctx.append = append;
551       unbash_spaces(store_name);
552       unbash_spaces(media_type);
553       unbash_spaces(pool_name);
554       unbash_spaces(pool_type);
555       store = new DIRSTORE;
556       dirstore->append(store);
557       memset(store, 0, sizeof(DIRSTORE));
558       store->device = New(alist(10));
559       bstrncpy(store->name, store_name, sizeof(store->name));
560       bstrncpy(store->media_type, media_type, sizeof(store->media_type));
561       bstrncpy(store->pool_name, pool_name, sizeof(store->pool_name));
562       bstrncpy(store->pool_type, pool_type, sizeof(store->pool_type));
563       store->append = append;
564
565       /* Now get all devices */
566       while (dir->recv() >= 0) {
567          Dmsg1(100, "<dird device: %s", dir->msg);
568          ok = sscanf(dir->msg, use_device, dev_name.c_str()) == 1;
569          if (!ok) {
570             break;
571          }
572          unbash_spaces(dev_name);
573          store->device->append(bstrdup(dev_name.c_str()));
574       }
575    }  while (ok && dir->recv() >= 0);
576
577 #ifdef DEVELOPER
578    /* This loop is debug code and can be removed */
579    /* ***FIXME**** remove after 1.38 release */
580    char *device_name;
581    foreach_alist(store, dirstore) {
582       Dmsg5(110, "Storage=%s media_type=%s pool=%s pool_type=%s append=%d\n", 
583          store->name, store->media_type, store->pool_name, 
584          store->pool_type, store->append);
585       foreach_alist(device_name, store->device) {
586          Dmsg1(110, "   Device=%s\n", device_name);
587       }
588    }
589 #endif
590
591    init_jcr_device_wait_timers(jcr);
592    /*                    
593     * At this point, we have a list of all the Director's Storage
594     *  resources indicated for this Job, which include Pool, PoolType,
595     *  storage name, and Media type.     
596     * Then for each of the Storage resources, we have a list of
597     *  device names that were given.
598     *
599     * Wiffle through them and find one that can do the backup.
600     */
601    if (ok) {
602       bool first = true;           /* print wait message once */
603       bool fail = false;
604       rctx.notify_dir = true;
605       lock_reservations();
606       for ( ; !fail && !job_canceled(jcr); ) {
607          while ((msg = (char *)msgs->pop())) {
608             free(msg);
609          }
610          rctx.suitable_device = false;
611          rctx.have_volume = false;
612          rctx.VolumeName[0] = 0;
613          rctx.any_drive = false;
614          if (!jcr->PreferMountedVols) {
615             /* Look for unused drives in autochangers */
616             rctx.num_writers = 20000000;   /* start with impossible number */
617             rctx.low_use_drive = NULL;
618             rctx.PreferMountedVols = false;                
619             rctx.exact_match = false;
620             rctx.autochanger_only = true;
621             Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
622                rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
623                rctx.autochanger_only, rctx.any_drive);
624             if ((ok = find_suitable_device_for_job(jcr, rctx))) {
625                break;
626             }
627             /* Look through all drives possibly for low_use drive */
628             if (rctx.low_use_drive) {
629                rctx.try_low_use_drive = true;
630                if ((ok = find_suitable_device_for_job(jcr, rctx))) {
631                   break;
632                }
633                rctx.try_low_use_drive = false;
634             }
635             rctx.autochanger_only = false;
636             Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
637                rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
638                rctx.autochanger_only, rctx.any_drive);
639             if ((ok = find_suitable_device_for_job(jcr, rctx))) {
640                break;
641             }
642          }
643          /* Look for an exact match all drives */
644          rctx.PreferMountedVols = true;
645          rctx.exact_match = true;
646          rctx.autochanger_only = false;
647          Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
648             rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
649             rctx.autochanger_only, rctx.any_drive);
650          if ((ok = find_suitable_device_for_job(jcr, rctx))) {
651             break;
652          }
653          /* Look for any mounted drive */
654          rctx.exact_match = false;
655          Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
656             rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
657             rctx.autochanger_only, rctx.any_drive);
658          if ((ok = find_suitable_device_for_job(jcr, rctx))) {
659             break;
660          }
661          /* Try any drive */
662          rctx.any_drive = true;
663          Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
664             rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
665             rctx.autochanger_only, rctx.any_drive);
666          if ((ok = find_suitable_device_for_job(jcr, rctx))) {
667             break;
668          }
669          /* Keep reservations locked *except* during wait_for_device() */
670          unlock_reservations();
671          if (!rctx.suitable_device || !wait_for_device(jcr, first)) {
672             Dmsg0(100, "Fail. !suitable_device || !wait_for_device\n");
673             fail = true;
674          }   
675          lock_reservations();
676          first = false;
677          bnet_sig(dir, BNET_HEARTBEAT);  /* Inform Dir that we are alive */
678       }
679       unlock_reservations();
680       if (!ok) {
681          /*
682           * If we get here, there are no suitable devices available, which
683           *  means nothing configured.  If a device is suitable but busy
684           *  with another Volume, we will not come here.
685           */
686          unbash_spaces(dir->msg);
687          pm_strcpy(jcr->errmsg, dir->msg);
688          Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
689          Jmsg(jcr, M_FATAL, 0, _("\n"
690             "     Device \"%s\" with MediaType \"%s\" requested by DIR not found in SD Device resources.\n"),
691               dev_name.c_str(), media_type.c_str());
692          bnet_fsend(dir, NO_device, dev_name.c_str());
693
694          Dmsg1(100, ">dird: %s", dir->msg);
695       }
696    } else {
697       unbash_spaces(dir->msg);
698       pm_strcpy(jcr->errmsg, dir->msg);
699       Jmsg(jcr, M_FATAL, 0, _("Failed command: %s\n"), jcr->errmsg);
700       bnet_fsend(dir, BAD_use, jcr->errmsg);
701       Dmsg1(100, ">dird: %s", dir->msg);
702    }
703
704    release_msgs(jcr);
705    return ok;
706 }
707
708 void release_msgs(JCR *jcr)
709 {
710    alist *msgs = jcr->reserve_msgs;
711    char *msg;
712
713    if (!msgs) {
714       return;
715    }
716    lock_reservations();
717    while ((msg = (char *)msgs->pop())) {
718       free(msg);
719    }
720    delete msgs;
721    jcr->reserve_msgs = NULL;
722    unlock_reservations();
723 }
724
725 /*
726  * Search for a device suitable for this job.
727  */
728 bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx)
729 {
730    bool ok;
731    DIRSTORE *store;
732    char *device_name;
733    alist *dirstore;
734
735    if (rctx.append) {
736       dirstore = jcr->write_store;
737    } else {
738       dirstore = jcr->read_store;
739    }
740    /* 
741     * For each storage device that the user specified, we
742     *  search and see if there is a resource for that device.
743     */
744    Dmsg4(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d\n",
745       rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
746       rctx.autochanger_only);
747    ok = false;
748    foreach_alist(store, dirstore) {
749       rctx.store = store;
750       foreach_alist(device_name, store->device) {
751          int stat;
752          rctx.device_name = device_name;
753          stat = search_res_for_device(rctx); 
754          if (stat == 1) {             /* found available device */
755             Dmsg1(100, "Suitable device found=%s\n", device_name);
756             ok = true;
757             break;
758          } else if (stat == 0) {      /* device busy */
759             Dmsg1(110, "Suitable device=%s, busy: not use\n", device_name);
760          } else {
761             /* otherwise error */
762             Dmsg0(110, "No suitable device found.\n");
763          }
764       }
765       if (ok) {
766          break;
767       }
768    }
769    return ok;
770 }
771
772 /*
773  * Search for a particular storage device with particular storage
774  *  characteristics (MediaType).
775  */
776 int search_res_for_device(RCTX &rctx) 
777 {
778    AUTOCHANGER *changer;
779    BSOCK *dir = rctx.jcr->dir_bsock;
780    bool ok;
781    int stat;
782
783    Dmsg1(110, "Search res for %s\n", rctx.device_name);
784    /* Look through Autochangers first */
785    foreach_res(changer, R_AUTOCHANGER) {
786       Dmsg1(150, "Try match changer res=%s\n", changer->hdr.name);
787       /* Find resource, and make sure we were able to open it */
788       if (fnmatch(rctx.device_name, changer->hdr.name, 0) == 0) {
789          /* Try each device in this AutoChanger */
790          foreach_alist(rctx.device, changer->device) {
791             Dmsg1(110, "Try changer device %s\n", rctx.device->hdr.name);
792             stat = reserve_device(rctx);
793             if (stat != 1) {             /* try another device */
794                continue;
795             }
796             POOL_MEM dev_name;
797             if (rctx.store->append == SD_APPEND) {
798                Dmsg2(100, "Device %s reserved=%d for append.\n", rctx.device->hdr.name,
799                   rctx.jcr->dcr->dev->reserved_device);
800             } else {
801                Dmsg2(100, "Device %s reserved=%d for read.\n", rctx.device->hdr.name,
802                   rctx.jcr->read_dcr->dev->reserved_device);
803             }
804             if (rctx.notify_dir) {
805                pm_strcpy(dev_name, rctx.device->hdr.name);
806                bash_spaces(dev_name);
807                ok = bnet_fsend(dir, OK_device, dev_name.c_str());  /* Return real device name */
808                Dmsg1(100, ">dird changer: %s", dir->msg);
809             } else {
810                ok = true;
811             }
812             return ok ? 1 : -1;
813          }
814       }
815    }
816
817    /* Now if requested look through regular devices */
818    if (!rctx.autochanger_only) {
819       foreach_res(rctx.device, R_DEVICE) {
820          Dmsg1(150, "Try match res=%s\n", rctx.device->hdr.name);
821          /* Find resource, and make sure we were able to open it */
822          if (fnmatch(rctx.device_name, rctx.device->hdr.name, 0) == 0) {
823             stat = reserve_device(rctx);
824             if (stat != 1) {
825                return stat;
826             }
827             if (rctx.notify_dir) {
828                bash_spaces(rctx.device_name);
829                ok = bnet_fsend(dir, OK_device, rctx.device_name);
830                Dmsg1(100, ">dird dev: %s", dir->msg);
831             } else {
832                ok = true;
833             }
834             return ok ? 1 : -1;
835          }
836       }
837    }
838    return -1;                    /* nothing found */
839 }
840
841 /*
842  *  Try to reserve a specific device.
843  *
844  *  Returns: 1 -- OK, have DCR
845  *           0 -- must wait
846  *          -1 -- fatal error
847  */
848 static int reserve_device(RCTX &rctx)
849 {
850    bool ok;
851    DCR *dcr;
852    const int name_len = MAX_NAME_LENGTH;
853
854    /* Make sure MediaType is OK */
855    Dmsg2(110, "MediaType device=%s request=%s\n",
856          rctx.device->media_type, rctx.store->media_type);
857    if (strcmp(rctx.device->media_type, rctx.store->media_type) != 0) {
858       return -1;
859    }
860
861    /* Make sure device exists -- i.e. we can stat() it */
862    if (!rctx.device->dev) {
863       rctx.device->dev = init_dev(rctx.jcr, rctx.device);
864    }
865    if (!rctx.device->dev) {
866       if (rctx.device->changer_res) {
867         Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
868            "     Device \"%s\" in changer \"%s\" requested by DIR could not be opened or does not exist.\n"),
869              rctx.device->hdr.name, rctx.device_name);
870       } else {
871          Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
872             "     Device \"%s\" requested by DIR could not be opened or does not exist.\n"),
873               rctx.device_name);
874       }
875       return -1;  /* no use waiting */
876    }  
877
878    rctx.suitable_device = true;
879    Dmsg2(110, "Try reserve %s JobId=%u\n", rctx.device->hdr.name,
880          rctx.jcr->JobId);
881    dcr = new_dcr(rctx.jcr, rctx.device->dev);
882    if (!dcr) {
883       BSOCK *dir = rctx.jcr->dir_bsock;
884       bnet_fsend(dir, _("3926 Could not get dcr for device: %s\n"), rctx.device_name);
885       Dmsg1(100, ">dird: %s", dir->msg);
886       return -1;
887    }
888    bstrncpy(dcr->pool_name, rctx.store->pool_name, name_len);
889    bstrncpy(dcr->pool_type, rctx.store->pool_type, name_len);
890    bstrncpy(dcr->media_type, rctx.store->media_type, name_len);
891    bstrncpy(dcr->dev_name, rctx.device_name, name_len);
892    if (rctx.store->append == SD_APPEND) {
893       Dmsg2(100, "have_vol=%d vol=%s\n", rctx.have_volume, rctx.VolumeName);
894       if (!rctx.have_volume) {
895          dcr->any_volume = true;
896          if (dir_find_next_appendable_volume(dcr)) {
897             bstrncpy(rctx.VolumeName, dcr->VolumeName, sizeof(rctx.VolumeName));
898             Dmsg2(100, "JobId=%u looking for Volume=%s\n", (int)rctx.jcr->JobId, rctx.VolumeName);
899             rctx.have_volume = true;
900          } else {
901             Dmsg0(100, "No next volume found\n");
902             rctx.have_volume = false;
903             rctx.VolumeName[0] = 0;
904         }
905       }
906       ok = reserve_device_for_append(dcr, rctx);
907       if (ok) {
908          rctx.jcr->dcr = dcr;
909          Dmsg5(100, "Reserved=%d dev_name=%s mediatype=%s pool=%s ok=%d\n",
910                dcr->dev->reserved_device,
911                dcr->dev_name, dcr->media_type, dcr->pool_name, ok);
912       }
913    } else {
914       ok = reserve_device_for_read(dcr);
915       if (ok) {
916          rctx.jcr->read_dcr = dcr;
917          Dmsg5(100, "Read reserved=%d dev_name=%s mediatype=%s pool=%s ok=%d\n",
918                dcr->dev->reserved_device,
919                dcr->dev_name, dcr->media_type, dcr->pool_name, ok);
920       }
921    }
922    if (!ok) {
923       rctx.have_volume = false;
924       free_dcr(dcr);
925       Dmsg0(110, "Not OK.\n");
926       return 0;
927    }
928    return 1;
929 }
930
931 /*
932  * We "reserve" the drive by setting the ST_READ bit. No one else
933  *  should touch the drive until that is cleared.
934  *  This allows the DIR to "reserve" the device before actually
935  *  starting the job. 
936  */
937 static bool reserve_device_for_read(DCR *dcr)
938 {
939    DEVICE *dev = dcr->dev;
940    JCR *jcr = dcr->jcr;
941    bool ok = false;
942
943    ASSERT(dcr);
944
945    /* Get locks in correct order */
946    unlock_reservations();
947    dev->dlock();  
948    lock_reservations();
949
950    if (is_device_unmounted(dev)) {             
951       Dmsg1(200, "Device %s is BLOCKED due to user unmount.\n", dev->print_name());
952       Mmsg(jcr->errmsg, _("3601 JobId=%u device %s is BLOCKED due to user unmount.\n"),
953            jcr->JobId, dev->print_name());
954       queue_reserve_message(jcr);
955       goto bail_out;
956    }
957
958    if (dev->is_busy()) {
959       Dmsg4(200, "Device %s is busy ST_READ=%d num_writers=%d reserved=%d.\n", dev->print_name(),
960          dev->state & ST_READ?1:0, dev->num_writers, dev->reserved_device);
961       Mmsg(jcr->errmsg, _("3602 JobId=%u device %s is busy (already reading/writing).\n"),
962             jcr->JobId, dev->print_name());
963       queue_reserve_message(jcr);
964       goto bail_out;
965    }
966
967    dev->clear_append();
968    dev->set_read();
969    ok = true;
970    dev->reserved_device++;
971    Dmsg3(100, "Inc reserve=%d dev=%s %p\n", dev->reserved_device, 
972       dev->print_name(), dev);
973    dcr->reserved_device = true;
974
975 bail_out:
976    dev->dunlock();
977    return ok;
978 }
979
980
981 /*
982  * We reserve the device for appending by incrementing the 
983  *  reserved_device. We do virtually all the same work that
984  *  is done in acquire_device_for_append(), but we do
985  *  not attempt to mount the device. This routine allows
986  *  the DIR to reserve multiple devices before *really* 
987  *  starting the job. It also permits the SD to refuse 
988  *  certain devices (not up, ...).
989  *
990  * Note, in reserving a device, if the device is for the
991  *  same pool and the same pool type, then it is acceptable.
992  *  The Media Type has already been checked. If we are
993  *  the first tor reserve the device, we put the pool
994  *  name and pool type in the device record.
995  */
996 static bool reserve_device_for_append(DCR *dcr, RCTX &rctx)
997 {
998    JCR *jcr = dcr->jcr;
999    DEVICE *dev = dcr->dev;
1000    bool ok = false;
1001
1002    ASSERT(dcr);
1003
1004    /* Get locks in correct order */
1005    unlock_reservations();
1006    dev->dlock();
1007    lock_reservations();
1008
1009    /* If device is being read, we cannot write it */
1010    if (dev->can_read()) {
1011       Mmsg(jcr->errmsg, _("3603 JobId=%u device %s is busy reading.\n"), 
1012          jcr->JobId, dev->print_name());
1013       Dmsg1(110, "%s", jcr->errmsg);
1014       queue_reserve_message(jcr);
1015       goto bail_out;
1016    }
1017
1018    /* If device is unmounted, we are out of luck */
1019    if (is_device_unmounted(dev)) {
1020       Mmsg(jcr->errmsg, _("3604 JobId=%u device %s is BLOCKED due to user unmount.\n"), 
1021          jcr->JobId, dev->print_name());
1022       Dmsg1(110, "%s", jcr->errmsg);
1023       queue_reserve_message(jcr);
1024       goto bail_out;
1025    }
1026
1027    Dmsg1(110, "reserve_append device is %s\n", dev->is_tape()?"tape":"disk");
1028
1029    /* Now do detailed tests ... */
1030    if (can_reserve_drive(dcr, rctx) != 1) {
1031       Dmsg0(110, "can_reserve_drive!=1\n");
1032       goto bail_out;
1033    }
1034
1035    dev->reserved_device++;
1036    Dmsg3(100, "Inc reserve=%d dev=%s %p\n", dev->reserved_device, 
1037       dev->print_name(), dev);
1038    dcr->reserved_device = true;
1039    ok = true;
1040
1041 bail_out:
1042    dev->dunlock();
1043    return ok;
1044 }
1045
1046 /*
1047  * Returns: 1 if drive can be reserved
1048  *          0 if we should wait
1049  *         -1 on error or impossibility
1050  */
1051 static int can_reserve_drive(DCR *dcr, RCTX &rctx) 
1052 {
1053    DEVICE *dev = dcr->dev;
1054    JCR *jcr = dcr->jcr;
1055
1056    Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
1057          rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
1058          rctx.autochanger_only, rctx.any_drive);
1059
1060    /* setting any_drive overrides PreferMountedVols flag */
1061    if (!rctx.any_drive) {
1062       /*
1063        * When PreferMountedVols is set, we keep track of the 
1064        *  drive in use that has the least number of writers, then if
1065        *  no unmounted drive is found, we try that drive. This   
1066        *  helps spread the load to the least used drives.  
1067        */
1068       if (rctx.try_low_use_drive && dev == rctx.low_use_drive) {
1069          Dmsg3(110, "OK dev=%s == low_drive=%s. JobId=%u\n",
1070             dev->print_name(), rctx.low_use_drive->print_name(), jcr->JobId);
1071          return 1;
1072       }
1073       /* If he wants a free drive, but this one is busy, no go */
1074       if (!rctx.PreferMountedVols && dev->is_busy()) {
1075          /* Save least used drive */
1076          if ((dev->num_writers + dev->reserved_device) < rctx.num_writers) {
1077             rctx.num_writers = dev->num_writers + dev->reserved_device;
1078             rctx.low_use_drive = dev;
1079             Dmsg2(110, "set low use drive=%s num_writers=%d\n", dev->print_name(),
1080                rctx.num_writers);
1081          } else {
1082             Dmsg1(110, "not low use num_writers=%d\n", dev->num_writers+ 
1083                dev->reserved_device);
1084          }
1085          Dmsg1(110, "failed: !prefMnt && busy. JobId=%u\n", jcr->JobId);
1086          Mmsg(jcr->errmsg, _("3605 JobId=%u wants free drive but device %s is busy.\n"), 
1087             jcr->JobId, dev->print_name());
1088          queue_reserve_message(jcr);
1089          return 0;
1090       }
1091
1092       /* Check for prefer mounted volumes */
1093       if (rctx.PreferMountedVols && !dev->VolHdr.VolumeName[0] && dev->is_tape()) {
1094          Mmsg(jcr->errmsg, _("3606 JobId=%u prefers mounted drives, but drive %s has no Volume.\n"), 
1095             jcr->JobId, dev->print_name());
1096          queue_reserve_message(jcr);
1097          Dmsg1(110, "failed: want mounted -- no vol JobId=%u\n", jcr->JobId);
1098          return 0;                 /* No volume mounted */
1099       }
1100
1101       /* Check for exact Volume name match */
1102       if (rctx.exact_match && rctx.have_volume &&
1103           strcmp(dev->VolHdr.VolumeName, rctx.VolumeName) != 0) {
1104          Mmsg(jcr->errmsg, _("3607 JobId=%u wants Vol=\"%s\" drive has Vol=\"%s\" on drive %s.\n"), 
1105             jcr->JobId, rctx.VolumeName, dev->VolHdr.VolumeName, 
1106             dev->print_name());
1107          queue_reserve_message(jcr);
1108          Dmsg2(110, "failed: Not exact match have=%s want=%s\n",
1109                dev->VolHdr.VolumeName, rctx.VolumeName);
1110          return 0;
1111       }
1112    }
1113
1114    /* Check for unused autochanger drive */
1115    if (rctx.autochanger_only && dev->num_writers == 0 &&
1116        dev->VolHdr.VolumeName[0] == 0) {
1117       /* Device is available but not yet reserved, reserve it for us */
1118       Dmsg2(100, "OK Res Unused autochanger %s JobId=%u.\n",
1119          dev->print_name(), jcr->JobId);
1120       bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
1121       bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
1122       return 1;                       /* reserve drive */
1123    }
1124
1125    /*
1126     * Handle the case that there are no writers
1127     */
1128    if (dev->num_writers == 0) {
1129       /* Now check if there are any reservations on the drive */
1130       if (dev->reserved_device) {           
1131          /* Now check if we want the same Pool and pool type */
1132          if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
1133              strcmp(dev->pool_type, dcr->pool_type) == 0) {
1134             /* OK, compatible device */
1135             Dmsg2(100, "OK dev: %s num_writers=0, reserved, pool matches JobId=%u\n",
1136                dev->print_name(), jcr->JobId);
1137             return 1;
1138          } else {
1139             /* Drive Pool not suitable for us */
1140             Mmsg(jcr->errmsg, _(
1141 "3608 JobId=%u wants Pool=\"%s\" but have Pool=\"%s\" nreserve=%d on drive %s.\n"), 
1142                   jcr->JobId, dcr->pool_name, dev->pool_name,
1143                   dev->reserved_device, dev->print_name());
1144             queue_reserve_message(jcr);
1145             Dmsg2(110, "failed: busy num_writers=0, reserved, pool=%s wanted=%s\n",
1146                dev->pool_name, dcr->pool_name);
1147             return 0;                 /* wait */
1148          }
1149       } else if (dev->can_append()) {
1150          /* Device in append mode, check if changing pool */
1151          if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
1152              strcmp(dev->pool_type, dcr->pool_type) == 0) {
1153             Dmsg2(100, "OK dev: %s num_writers=0, can_append, pool matches. JobId=%u\n",
1154                dev->print_name(), jcr->JobId);
1155             /* OK, compatible device */
1156             return 1;
1157          } else {
1158             /* Changing pool, unload old tape if any in drive */
1159             Dmsg0(100, "OK dev: num_writers=0, not reserved, pool change, unload changer\n");
1160             unload_autochanger(dcr, 0);
1161          }
1162       }
1163       /* Device is available but not yet reserved, reserve it for us */
1164       Dmsg2(100, "OK Dev avail reserved %s JobId=%u\n", dev->print_name(),
1165          jcr->JobId);
1166       bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
1167       bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
1168       return 1;                       /* reserve drive */
1169    }
1170
1171    /*
1172     * Check if the device is in append mode with writers (i.e.
1173     *  available if pool is the same).
1174     */
1175    if (dev->can_append() || dev->num_writers > 0) {
1176       /* Yes, now check if we want the same Pool and pool type */
1177       if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
1178           strcmp(dev->pool_type, dcr->pool_type) == 0) {
1179          Dmsg2(100, "OK dev: %s num_writers>=0, can_append, pool matches. JobId=%u\n",
1180             dev->print_name(), jcr->JobId);
1181          /* OK, compatible device */
1182          return 1;
1183       } else {
1184          /* Drive Pool not suitable for us */
1185          Mmsg(jcr->errmsg, _("3609 JobId=%u wants Pool=\"%s\" but has Pool=\"%s\" on drive %s.\n"), 
1186                jcr->JobId, dcr->pool_name, dev->pool_name, dev->print_name());
1187          queue_reserve_message(jcr);
1188          Dmsg2(110, "failed: busy num_writers>0, can_append, pool=%s wanted=%s\n",
1189             dev->pool_name, dcr->pool_name);
1190          return 0;                    /* wait */
1191       }
1192    } else {
1193       Pmsg0(000, _("Logic error!!!! Should not get here.\n"));
1194       Mmsg(jcr->errmsg, _("3910 JobId=%u Logic error!!!! drive %s Should not get here.\n"),
1195             jcr->JobId, dev->print_name());
1196       queue_reserve_message(jcr);
1197       Jmsg0(jcr, M_FATAL, 0, _("Logic error!!!! Should not get here.\n"));
1198       return -1;                      /* error, should not get here */
1199    }
1200    Mmsg(jcr->errmsg, _("3911 JobId=%u failed reserve drive %s.\n"), 
1201          jcr->JobId, dev->print_name());
1202    queue_reserve_message(jcr);
1203    Dmsg2(110, "failed: No reserve %s JobId=%u\n", dev->print_name(), jcr->JobId);
1204    return 0;
1205 }
1206
1207 /*
1208  * search_lock is already set on entering this routine 
1209  */
1210 static void queue_reserve_message(JCR *jcr)
1211 {
1212    int i;   
1213    alist *msgs = jcr->reserve_msgs;
1214    char *msg;
1215
1216    if (!msgs) {
1217       return;
1218    }
1219    /*
1220     * Look for duplicate message.  If found, do
1221     * not insert
1222     */
1223    for (i=msgs->size()-1; i >= 0; i--) {
1224       msg = (char *)msgs->get(i);
1225       if (!msg) {
1226          return;
1227       }
1228       /* Comparison based on 4 digit message number */
1229       if (strncmp(msg, jcr->errmsg, 4) == 0) {
1230          return;
1231       }
1232    }      
1233    /* Message unique, so insert it */
1234    jcr->reserve_msgs->push(bstrdup(jcr->errmsg));
1235 }
1236
1237 /*
1238  * Send any reservation messages queued for this jcr
1239  */
1240 void send_drive_reserve_messages(JCR *jcr, void sendit(const char *msg, int len, void *sarg), void *arg)
1241 {
1242    int i;
1243    alist *msgs;
1244    char *msg;
1245
1246    lock_reservations();
1247    msgs = jcr->reserve_msgs;
1248    if (!msgs || msgs->size() == 0) {
1249       unlock_reservations();
1250       return;
1251    }
1252    for (i=msgs->size()-1; i >= 0; i--) {
1253       msg = (char *)msgs->get(i);
1254       if (msg) {
1255          sendit("   ", 3, arg);
1256          sendit(msg, strlen(msg), arg);
1257       } else {
1258          break;
1259       }
1260    }
1261    unlock_reservations();
1262 }