]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/reserve.c
kes Start implementing bstrerror() in place of strerror().
[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.bstrerror(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.bstrerror(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.bstrerror(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(0);
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 volume busy on another drive
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    P(vol_list_lock);
283    debug_list_volumes("begin reserve_volume", debug_nolock);
284    /* 
285     * First, remove any old volume attached to this device as it
286     *  is no longer used.
287     */
288    if (dev->vol) {
289       vol = dev->vol;
290       /*
291        * Make sure we don't remove the current volume we are inserting
292        *  because it was probably inserted by another job.
293        */
294       if (strcmp(vol->vol_name, VolumeName) == 0) {
295          goto get_out;                  /* Volume already on this device */
296       } else {
297          Dmsg3(100, "reserve_vol free vol=%s at %p JobId=%u\n", vol->vol_name,
298                vol->vol_name, (int)dcr->jcr->JobId);
299          debug_list_volumes("reserve_vol free", debug_nolock);
300          vol_list->remove(vol);
301          free_vol_item(vol);
302       }
303    }
304
305    /* Create a new Volume entry */
306    nvol = new_vol_item(dcr, VolumeName);
307
308    /*
309     * Now try to insert the new Volume
310     */
311    vol = (VOLRES *)vol_list->binary_insert(nvol, my_compare);
312    if (vol != nvol) {
313       Dmsg2(100, "Found vol=%s dev-same=%d\n", vol->vol_name, dev==vol->dev);
314       /*
315        * At this point, a Volume with this name already is in the list,
316        *   so we simply release our new Volume entry. Note, this should
317        *   only happen if we are moving the volume from one drive to another.
318        */
319       Dmsg3(100, "reserve_vol free-tmp vol=%s at %p JobId=%u\n", vol->vol_name,
320             vol->vol_name, (int)dcr->jcr->JobId);
321       /*
322        * Clear dev pointer so that free_vol_item() doesn't 
323        *  take away our volume. 
324        */
325       nvol->dev = NULL;                   /* don't zap dev entry */
326       free_vol_item(nvol);
327
328       /* Check if we are trying to use the Volume on a different drive */
329       if (dev != vol->dev) {
330          /* Caller wants to switch Volume to another device */
331          if (!vol->dev->is_busy()) {
332             /* OK to move it -- I'm not sure this will work */
333             Dmsg3(100, "==== Swap vol=%s from dev=%s to %s\n", VolumeName,
334                vol->dev->print_name(), dev->print_name());
335             vol->dev->vol = NULL;         /* take vol from old drive */
336             vol->dev->VolHdr.VolumeName[0] = 0;
337             vol->dev = dev;               /* point vol at new drive */
338             dev->vol = vol;               /* point dev at vol */
339             dev->VolHdr.VolumeName[0] = 0;
340          } else {
341             Dmsg3(100, "Volume busy could not swap vol=%s from dev=%s to %s\n", VolumeName,
342                vol->dev->print_name(), dev->print_name());
343             vol = NULL;                /* device busy */
344          }
345       }
346    }
347    dev->vol = vol;
348
349 get_out:
350    debug_list_volumes("end new volume", debug_nolock);
351    V(vol_list_lock);
352    return vol;
353 }
354
355 /*
356  * Search for a Volume name in the Volume list.
357  *
358  *  Returns: VOLRES entry on success
359  *           NULL if the Volume is not in the list
360  */
361 VOLRES *find_volume(const char *VolumeName)
362 {
363    VOLRES vol, *fvol;
364    /* Do not lock reservations here */
365    P(vol_list_lock);
366    vol.vol_name = bstrdup(VolumeName);
367    fvol = (VOLRES *)vol_list->binary_search(&vol, my_compare);
368    free(vol.vol_name);
369    Dmsg2(100, "find_vol=%s found=%d\n", VolumeName, fvol!=NULL);
370    debug_list_volumes("find_volume", debug_nolock);
371    V(vol_list_lock);
372    return fvol;
373 }
374
375 /* 
376  * Remove any reservation from a drive and tell the system
377  *  that the volume is unused at least by us.
378  */
379 void unreserve_device(DCR *dcr)
380 {
381    DEVICE *dev = dcr->dev;
382    dev->dlock();
383    if (dcr->reserved_device) {
384       dcr->reserved_device = false;
385       dev->reserved_device--;
386       Dmsg2(100, "Dec reserve=%d dev=%s\n", dev->reserved_device, dev->print_name());
387       dcr->reserved_device = false;
388       /* If we set read mode in reserving, remove it */
389       if (dev->can_read()) {
390          dev->clear_read();
391       }
392       if (dev->num_writers < 0) {
393          Jmsg1(dcr->jcr, M_ERROR, 0, _("Hey! num_writers=%d!!!!\n"), dev->num_writers);
394          dev->num_writers = 0;
395       }
396    }
397
398    volume_unused(dcr);
399    dev->dunlock();
400 }
401
402 /*  
403  * Free a Volume from the Volume list if it is no longer used
404  *
405  *  Returns: true if the Volume found and removed from the list
406  *           false if the Volume is not in the list or is in use
407  */
408 bool volume_unused(DCR *dcr)
409 {
410    DEVICE *dev = dcr->dev;
411
412    if (dev->vol == NULL) {
413       Dmsg1(100, " unreserve_volume: no vol on %s\n", dev->print_name());
414       debug_list_volumes("null return unreserve_volume", debug_lock);
415       return false;
416    }
417
418    if (dev->is_busy()) {
419       Dmsg1(100, "unreserve_volume: dev is busy %s\n", dev->print_name());
420       debug_list_volumes("dev busy return unreserve_volume", debug_lock);
421       return false;
422    }
423
424    return free_volume(dev);
425 }
426
427 /*
428  * Unconditionally release the volume
429  */
430 bool free_volume(DEVICE *dev)
431 {
432    VOLRES *vol;
433
434    if (dev->vol == NULL) {
435       Dmsg1(100, "No vol on dev %s\n", dev->print_name());
436       return false;
437    }
438    P(vol_list_lock);
439    vol = dev->vol;
440    dev->vol = NULL;
441    Dmsg1(100, "free_volume %s\n", vol->vol_name);
442    vol_list->remove(vol);
443    Dmsg3(100, "free_volume %s at %p dev=%s\n", vol->vol_name, vol->vol_name,
444          dev->print_name());
445    free_vol_item(vol);
446    debug_list_volumes("free_volume", debug_nolock);
447    V(vol_list_lock);
448    return vol != NULL;
449 }
450
451       
452 /* Create the Volume list */
453 void create_volume_list()
454 {
455    VOLRES *dummy = NULL;
456    if (vol_list == NULL) {
457       vol_list = New(dlist(dummy, &dummy->link));
458    }
459 }
460
461 /* Release all Volumes from the list */
462 void free_volume_list()
463 {
464    VOLRES *vol;
465    if (!vol_list) {
466       return;
467    }
468    P(vol_list_lock);
469    for (vol=(VOLRES *)vol_list->first(); vol; vol=(VOLRES *)vol_list->next(vol)) {
470       Dmsg2(100, "Unreleased Volume=%s dev=%p\n", vol->vol_name, vol->dev);
471       free(vol->vol_name);
472       vol->vol_name = NULL;
473    }
474    delete vol_list;
475    vol_list = NULL;
476    V(vol_list_lock);
477 }
478
479 bool is_volume_in_use(DCR *dcr)
480 {
481    VOLRES *vol = find_volume(dcr->VolumeName);
482    if (!vol) {
483       Dmsg1(100, "Vol=%s not in use.\n", dcr->VolumeName);
484       return false;                   /* vol not in list */
485    }
486    ASSERT(vol->dev != NULL);
487
488    if (dcr->dev == vol->dev) {        /* same device OK */
489       Dmsg1(100, "Vol=%s on same dev.\n", dcr->VolumeName);
490       return false;
491    }
492    if (!vol->dev->is_busy()) {
493       Dmsg2(100, "Vol=%s dev=%s not busy.\n", dcr->VolumeName, vol->dev->print_name());
494       return false;
495    }
496    Dmsg2(100, "Vol=%s used by %s.\n", dcr->VolumeName, vol->dev->print_name());
497    return true;
498 }
499
500
501 /*
502  * We get the following type of information:
503  *
504  * use storage=xxx media_type=yyy pool_name=xxx pool_type=yyy append=1 copy=0 strip=0
505  *  use device=zzz
506  *  use device=aaa
507  *  use device=bbb
508  * use storage=xxx media_type=yyy pool_name=xxx pool_type=yyy append=0 copy=0 strip=0
509  *  use device=bbb
510  *
511  */
512 static bool use_storage_cmd(JCR *jcr)
513 {
514    POOL_MEM store_name, dev_name, media_type, pool_name, pool_type;
515    BSOCK *dir = jcr->dir_bsock;
516    int append;
517    bool ok;       
518    int Copy, Stripe;
519    DIRSTORE *store;
520    RCTX rctx;
521    char *msg;
522    alist *msgs;
523    alist *dirstore;
524
525    memset(&rctx, 0, sizeof(RCTX));
526    rctx.jcr = jcr;
527    /*
528     * If there are multiple devices, the director sends us
529     *   use_device for each device that it wants to use.
530     */
531    dirstore = New(alist(10, not_owned_by_alist));
532 // Dmsg2(000, "dirstore=%p JobId=%u\n", dirstore, jcr->JobId);
533    msgs = jcr->reserve_msgs = New(alist(10, not_owned_by_alist));  
534    do {
535       Dmsg1(100, "<dird: %s", dir->msg);
536       ok = sscanf(dir->msg, use_storage, store_name.c_str(), 
537                   media_type.c_str(), pool_name.c_str(), 
538                   pool_type.c_str(), &append, &Copy, &Stripe) == 7;
539       if (!ok) {
540          break;
541       }
542       if (append) {
543          jcr->write_store = dirstore;
544       } else {
545          jcr->read_store = dirstore;
546       }
547       rctx.append = append;
548       unbash_spaces(store_name);
549       unbash_spaces(media_type);
550       unbash_spaces(pool_name);
551       unbash_spaces(pool_type);
552       store = new DIRSTORE;
553       dirstore->append(store);
554       memset(store, 0, sizeof(DIRSTORE));
555       store->device = New(alist(10));
556       bstrncpy(store->name, store_name, sizeof(store->name));
557       bstrncpy(store->media_type, media_type, sizeof(store->media_type));
558       bstrncpy(store->pool_name, pool_name, sizeof(store->pool_name));
559       bstrncpy(store->pool_type, pool_type, sizeof(store->pool_type));
560       store->append = append;
561
562       /* Now get all devices */
563       while (dir->recv() >= 0) {
564          Dmsg1(100, "<dird device: %s", dir->msg);
565          ok = sscanf(dir->msg, use_device, dev_name.c_str()) == 1;
566          if (!ok) {
567             break;
568          }
569          unbash_spaces(dev_name);
570          store->device->append(bstrdup(dev_name.c_str()));
571       }
572    }  while (ok && dir->recv() >= 0);
573
574 #ifdef DEVELOPER
575    /* This loop is debug code and can be removed */
576    /* ***FIXME**** remove after 1.38 release */
577    char *device_name;
578    foreach_alist(store, dirstore) {
579       Dmsg5(110, "Storage=%s media_type=%s pool=%s pool_type=%s append=%d\n", 
580          store->name, store->media_type, store->pool_name, 
581          store->pool_type, store->append);
582       foreach_alist(device_name, store->device) {
583          Dmsg1(110, "   Device=%s\n", device_name);
584       }
585    }
586 #endif
587
588    init_jcr_device_wait_timers(jcr);
589    /*                    
590     * At this point, we have a list of all the Director's Storage
591     *  resources indicated for this Job, which include Pool, PoolType,
592     *  storage name, and Media type.     
593     * Then for each of the Storage resources, we have a list of
594     *  device names that were given.
595     *
596     * Wiffle through them and find one that can do the backup.
597     */
598    if (ok) {
599       int retries = 0;                /* wait for device retries */
600       bool fail = false;
601       rctx.notify_dir = true;
602       lock_reservations();
603       for ( ; !fail && !job_canceled(jcr); ) {
604          while ((msg = (char *)msgs->pop())) {
605             free(msg);
606          }
607          rctx.suitable_device = false;
608          rctx.have_volume = false;
609          rctx.VolumeName[0] = 0;
610          rctx.any_drive = false;
611          if (!jcr->PreferMountedVols) {
612             /* Look for unused drives in autochangers */
613             rctx.num_writers = 20000000;   /* start with impossible number */
614             rctx.low_use_drive = NULL;
615             rctx.PreferMountedVols = false;                
616             rctx.exact_match = false;
617             rctx.autochanger_only = true;
618             Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
619                rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
620                rctx.autochanger_only, rctx.any_drive);
621             if ((ok = find_suitable_device_for_job(jcr, rctx))) {
622                break;
623             }
624             /* Look through all drives possibly for low_use drive */
625             if (rctx.low_use_drive) {
626                rctx.try_low_use_drive = true;
627                if ((ok = find_suitable_device_for_job(jcr, rctx))) {
628                   break;
629                }
630                rctx.try_low_use_drive = false;
631             }
632             rctx.autochanger_only = false;
633             Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
634                rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
635                rctx.autochanger_only, rctx.any_drive);
636             if ((ok = find_suitable_device_for_job(jcr, rctx))) {
637                break;
638             }
639          }
640          /* Look for an exact match all drives */
641          rctx.PreferMountedVols = true;
642          rctx.exact_match = true;
643          rctx.autochanger_only = false;
644          Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
645             rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
646             rctx.autochanger_only, rctx.any_drive);
647          if ((ok = find_suitable_device_for_job(jcr, rctx))) {
648             break;
649          }
650          /* Look for any mounted drive */
651          rctx.exact_match = false;
652          Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
653             rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
654             rctx.autochanger_only, rctx.any_drive);
655          if ((ok = find_suitable_device_for_job(jcr, rctx))) {
656             break;
657          }
658          /* Try any drive */
659          rctx.any_drive = true;
660          Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
661             rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
662             rctx.autochanger_only, rctx.any_drive);
663          if ((ok = find_suitable_device_for_job(jcr, rctx))) {
664             break;
665          }
666          /* Keep reservations locked *except* during wait_for_device() */
667          unlock_reservations();
668          if (!rctx.suitable_device || !wait_for_device(jcr, retries)) {
669             Dmsg0(100, "Fail. !suitable_device || !wait_for_device\n");
670             fail = true;
671          }   
672          lock_reservations();
673          bnet_sig(dir, BNET_HEARTBEAT);  /* Inform Dir that we are alive */
674       }
675       unlock_reservations();
676       if (!ok) {
677          /*
678           * If we get here, there are no suitable devices available, which
679           *  means nothing configured.  If a device is suitable but busy
680           *  with another Volume, we will not come here.
681           */
682          unbash_spaces(dir->msg);
683          pm_strcpy(jcr->errmsg, dir->msg);
684          Jmsg(jcr, M_INFO, 0, _("Failed command: %s\n"), jcr->errmsg);
685          Jmsg(jcr, M_FATAL, 0, _("\n"
686             "     Device \"%s\" with MediaType \"%s\" requested by DIR not found in SD Device resources.\n"),
687               dev_name.c_str(), media_type.c_str());
688          bnet_fsend(dir, NO_device, dev_name.c_str());
689
690          Dmsg1(100, ">dird: %s", dir->msg);
691       }
692    } else {
693       unbash_spaces(dir->msg);
694       pm_strcpy(jcr->errmsg, dir->msg);
695       Jmsg(jcr, M_FATAL, 0, _("Failed command: %s\n"), jcr->errmsg);
696       bnet_fsend(dir, BAD_use, jcr->errmsg);
697       Dmsg1(100, ">dird: %s", dir->msg);
698    }
699
700    release_msgs(jcr);
701    return ok;
702 }
703
704 void release_msgs(JCR *jcr)
705 {
706    alist *msgs = jcr->reserve_msgs;
707    char *msg;
708
709    if (!msgs) {
710       return;
711    }
712    lock_reservations();
713    while ((msg = (char *)msgs->pop())) {
714       free(msg);
715    }
716    delete msgs;
717    jcr->reserve_msgs = NULL;
718    unlock_reservations();
719 }
720
721 /*
722  * Search for a device suitable for this job.
723  */
724 bool find_suitable_device_for_job(JCR *jcr, RCTX &rctx)
725 {
726    bool ok;
727    DIRSTORE *store;
728    char *device_name;
729    alist *dirstore;
730
731    if (rctx.append) {
732       dirstore = jcr->write_store;
733    } else {
734       dirstore = jcr->read_store;
735    }
736    /* 
737     * For each storage device that the user specified, we
738     *  search and see if there is a resource for that device.
739     */
740    Dmsg4(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d\n",
741       rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
742       rctx.autochanger_only);
743    ok = false;
744    foreach_alist(store, dirstore) {
745       rctx.store = store;
746       foreach_alist(device_name, store->device) {
747          int stat;
748          rctx.device_name = device_name;
749          stat = search_res_for_device(rctx); 
750          if (stat == 1) {             /* found available device */
751             Dmsg1(100, "Suitable device found=%s\n", device_name);
752             ok = true;
753             break;
754          } else if (stat == 0) {      /* device busy */
755             Dmsg1(110, "Suitable device=%s, busy: not use\n", device_name);
756          } else {
757             /* otherwise error */
758             Dmsg0(110, "No suitable device found.\n");
759          }
760       }
761       if (ok) {
762          break;
763       }
764    }
765    return ok;
766 }
767
768 /*
769  * Search for a particular storage device with particular storage
770  *  characteristics (MediaType).
771  */
772 int search_res_for_device(RCTX &rctx) 
773 {
774    AUTOCHANGER *changer;
775    BSOCK *dir = rctx.jcr->dir_bsock;
776    bool ok;
777    int stat;
778
779    Dmsg1(110, "Search res for %s\n", rctx.device_name);
780    /* Look through Autochangers first */
781    foreach_res(changer, R_AUTOCHANGER) {
782       Dmsg1(150, "Try match changer res=%s\n", changer->hdr.name);
783       /* Find resource, and make sure we were able to open it */
784       if (fnmatch(rctx.device_name, changer->hdr.name, 0) == 0) {
785          /* Try each device in this AutoChanger */
786          foreach_alist(rctx.device, changer->device) {
787             Dmsg1(110, "Try changer device %s\n", rctx.device->hdr.name);
788             stat = reserve_device(rctx);
789             if (stat != 1) {             /* try another device */
790                continue;
791             }
792             POOL_MEM dev_name;
793             if (rctx.store->append == SD_APPEND) {
794                Dmsg2(100, "Device %s reserved=%d for append.\n", rctx.device->hdr.name,
795                   rctx.jcr->dcr->dev->reserved_device);
796             } else {
797                Dmsg2(100, "Device %s reserved=%d for read.\n", rctx.device->hdr.name,
798                   rctx.jcr->read_dcr->dev->reserved_device);
799             }
800             if (rctx.notify_dir) {
801                pm_strcpy(dev_name, rctx.device->hdr.name);
802                bash_spaces(dev_name);
803                ok = bnet_fsend(dir, OK_device, dev_name.c_str());  /* Return real device name */
804                Dmsg1(100, ">dird changer: %s", dir->msg);
805             } else {
806                ok = true;
807             }
808             return ok ? 1 : -1;
809          }
810       }
811    }
812
813    /* Now if requested look through regular devices */
814    if (!rctx.autochanger_only) {
815       foreach_res(rctx.device, R_DEVICE) {
816          Dmsg1(150, "Try match res=%s\n", rctx.device->hdr.name);
817          /* Find resource, and make sure we were able to open it */
818          if (fnmatch(rctx.device_name, rctx.device->hdr.name, 0) == 0) {
819             stat = reserve_device(rctx);
820             if (stat != 1) {
821                return stat;
822             }
823             if (rctx.notify_dir) {
824                bash_spaces(rctx.device_name);
825                ok = bnet_fsend(dir, OK_device, rctx.device_name);
826                Dmsg1(100, ">dird dev: %s", dir->msg);
827             } else {
828                ok = true;
829             }
830             return ok ? 1 : -1;
831          }
832       }
833    }
834    return -1;                    /* nothing found */
835 }
836
837 /*
838  *  Try to reserve a specific device.
839  *
840  *  Returns: 1 -- OK, have DCR
841  *           0 -- must wait
842  *          -1 -- fatal error
843  */
844 static int reserve_device(RCTX &rctx)
845 {
846    bool ok;
847    DCR *dcr;
848    const int name_len = MAX_NAME_LENGTH;
849
850    /* Make sure MediaType is OK */
851    Dmsg2(110, "MediaType device=%s request=%s\n",
852          rctx.device->media_type, rctx.store->media_type);
853    if (strcmp(rctx.device->media_type, rctx.store->media_type) != 0) {
854       return -1;
855    }
856
857    /* Make sure device exists -- i.e. we can stat() it */
858    if (!rctx.device->dev) {
859       rctx.device->dev = init_dev(rctx.jcr, rctx.device);
860    }
861    if (!rctx.device->dev) {
862       if (rctx.device->changer_res) {
863         Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
864            "     Device \"%s\" in changer \"%s\" requested by DIR could not be opened or does not exist.\n"),
865              rctx.device->hdr.name, rctx.device_name);
866       } else {
867          Jmsg(rctx.jcr, M_WARNING, 0, _("\n"
868             "     Device \"%s\" requested by DIR could not be opened or does not exist.\n"),
869               rctx.device_name);
870       }
871       return -1;  /* no use waiting */
872    }  
873
874    rctx.suitable_device = true;
875    Dmsg2(110, "Try reserve %s JobId=%u\n", rctx.device->hdr.name,
876          rctx.jcr->JobId);
877    dcr = new_dcr(rctx.jcr, rctx.device->dev);
878    if (!dcr) {
879       BSOCK *dir = rctx.jcr->dir_bsock;
880       bnet_fsend(dir, _("3926 Could not get dcr for device: %s\n"), rctx.device_name);
881       Dmsg1(100, ">dird: %s", dir->msg);
882       return -1;
883    }
884    bstrncpy(dcr->pool_name, rctx.store->pool_name, name_len);
885    bstrncpy(dcr->pool_type, rctx.store->pool_type, name_len);
886    bstrncpy(dcr->media_type, rctx.store->media_type, name_len);
887    bstrncpy(dcr->dev_name, rctx.device_name, name_len);
888    if (rctx.store->append == SD_APPEND) {
889       Dmsg2(100, "have_vol=%d vol=%s\n", rctx.have_volume, rctx.VolumeName);
890       if (!rctx.have_volume) {
891          dcr->any_volume = true;
892          if (dir_find_next_appendable_volume(dcr)) {
893             bstrncpy(rctx.VolumeName, dcr->VolumeName, sizeof(rctx.VolumeName));
894             Dmsg2(100, "JobId=%u looking for Volume=%s\n", (int)rctx.jcr->JobId, rctx.VolumeName);
895             rctx.have_volume = true;
896          } else {
897             Dmsg0(100, "No next volume found\n");
898             rctx.have_volume = false;
899             rctx.VolumeName[0] = 0;
900         }
901       }
902       ok = reserve_device_for_append(dcr, rctx);
903       if (ok) {
904          rctx.jcr->dcr = dcr;
905          Dmsg5(100, "Reserved=%d dev_name=%s mediatype=%s pool=%s ok=%d\n",
906                dcr->dev->reserved_device,
907                dcr->dev_name, dcr->media_type, dcr->pool_name, ok);
908       }
909    } else {
910       ok = reserve_device_for_read(dcr);
911       if (ok) {
912          rctx.jcr->read_dcr = dcr;
913          Dmsg5(100, "Read reserved=%d dev_name=%s mediatype=%s pool=%s ok=%d\n",
914                dcr->dev->reserved_device,
915                dcr->dev_name, dcr->media_type, dcr->pool_name, ok);
916       }
917    }
918    if (!ok) {
919       rctx.have_volume = false;
920       free_dcr(dcr);
921       Dmsg0(110, "Not OK.\n");
922       return 0;
923    }
924    return 1;
925 }
926
927 /*
928  * We "reserve" the drive by setting the ST_READ bit. No one else
929  *  should touch the drive until that is cleared.
930  *  This allows the DIR to "reserve" the device before actually
931  *  starting the job. 
932  */
933 static bool reserve_device_for_read(DCR *dcr)
934 {
935    DEVICE *dev = dcr->dev;
936    JCR *jcr = dcr->jcr;
937    bool ok = false;
938
939    ASSERT(dcr);
940
941    dev->dlock();  
942
943    if (is_device_unmounted(dev)) {             
944       Dmsg1(200, "Device %s is BLOCKED due to user unmount.\n", dev->print_name());
945       Mmsg(jcr->errmsg, _("3601 JobId=%u device %s is BLOCKED due to user unmount.\n"),
946            jcr->JobId, dev->print_name());
947       queue_reserve_message(jcr);
948       goto bail_out;
949    }
950
951    if (dev->is_busy()) {
952       Dmsg4(200, "Device %s is busy ST_READ=%d num_writers=%d reserved=%d.\n", dev->print_name(),
953          dev->state & ST_READ?1:0, dev->num_writers, dev->reserved_device);
954       Mmsg(jcr->errmsg, _("3602 JobId=%u device %s is busy (already reading/writing).\n"),
955             jcr->JobId, dev->print_name());
956       queue_reserve_message(jcr);
957       goto bail_out;
958    }
959
960    dev->clear_append();
961    dev->set_read();
962    ok = true;
963    dev->reserved_device++;
964    Dmsg3(100, "Inc reserve=%d dev=%s %p\n", dev->reserved_device, 
965       dev->print_name(), dev);
966    dcr->reserved_device = true;
967
968 bail_out:
969    dev->dunlock();
970    return ok;
971 }
972
973
974 /*
975  * We reserve the device for appending by incrementing the 
976  *  reserved_device. We do virtually all the same work that
977  *  is done in acquire_device_for_append(), but we do
978  *  not attempt to mount the device. This routine allows
979  *  the DIR to reserve multiple devices before *really* 
980  *  starting the job. It also permits the SD to refuse 
981  *  certain devices (not up, ...).
982  *
983  * Note, in reserving a device, if the device is for the
984  *  same pool and the same pool type, then it is acceptable.
985  *  The Media Type has already been checked. If we are
986  *  the first tor reserve the device, we put the pool
987  *  name and pool type in the device record.
988  */
989 static bool reserve_device_for_append(DCR *dcr, RCTX &rctx)
990 {
991    JCR *jcr = dcr->jcr;
992    DEVICE *dev = dcr->dev;
993    bool ok = false;
994
995    ASSERT(dcr);
996
997    dev->dlock();
998
999    /* If device is being read, we cannot write it */
1000    if (dev->can_read()) {
1001       Mmsg(jcr->errmsg, _("3603 JobId=%u device %s is busy reading.\n"), 
1002          jcr->JobId, dev->print_name());
1003       Dmsg1(110, "%s", jcr->errmsg);
1004       queue_reserve_message(jcr);
1005       goto bail_out;
1006    }
1007
1008    /* If device is unmounted, we are out of luck */
1009    if (is_device_unmounted(dev)) {
1010       Mmsg(jcr->errmsg, _("3604 JobId=%u device %s is BLOCKED due to user unmount.\n"), 
1011          jcr->JobId, dev->print_name());
1012       Dmsg1(110, "%s", jcr->errmsg);
1013       queue_reserve_message(jcr);
1014       goto bail_out;
1015    }
1016
1017    Dmsg1(110, "reserve_append device is %s\n", dev->is_tape()?"tape":"disk");
1018
1019    /* Now do detailed tests ... */
1020    if (can_reserve_drive(dcr, rctx) != 1) {
1021       Dmsg0(110, "can_reserve_drive!=1\n");
1022       goto bail_out;
1023    }
1024
1025    dev->reserved_device++;
1026    Dmsg3(100, "Inc reserve=%d dev=%s %p\n", dev->reserved_device, 
1027       dev->print_name(), dev);
1028    dcr->reserved_device = true;
1029    ok = true;
1030
1031 bail_out:
1032    dev->dunlock();
1033    return ok;
1034 }
1035
1036 /*
1037  * Returns: 1 if drive can be reserved
1038  *          0 if we should wait
1039  *         -1 on error or impossibility
1040  */
1041 static int can_reserve_drive(DCR *dcr, RCTX &rctx) 
1042 {
1043    DEVICE *dev = dcr->dev;
1044    JCR *jcr = dcr->jcr;
1045
1046    Dmsg5(110, "PrefMnt=%d exact=%d suitable=%d chgronly=%d any=%d\n",
1047          rctx.PreferMountedVols, rctx.exact_match, rctx.suitable_device,
1048          rctx.autochanger_only, rctx.any_drive);
1049
1050    /* setting any_drive overrides PreferMountedVols flag */
1051    if (!rctx.any_drive) {
1052       /*
1053        * When PreferMountedVols is set, we keep track of the 
1054        *  drive in use that has the least number of writers, then if
1055        *  no unmounted drive is found, we try that drive. This   
1056        *  helps spread the load to the least used drives.  
1057        */
1058       if (rctx.try_low_use_drive && dev == rctx.low_use_drive) {
1059          Dmsg3(110, "OK dev=%s == low_drive=%s. JobId=%u\n",
1060             dev->print_name(), rctx.low_use_drive->print_name(), jcr->JobId);
1061          return 1;
1062       }
1063       /* If he wants a free drive, but this one is busy, no go */
1064       if (!rctx.PreferMountedVols && dev->is_busy()) {
1065          /* Save least used drive */
1066          if ((dev->num_writers + dev->reserved_device) < rctx.num_writers) {
1067             rctx.num_writers = dev->num_writers + dev->reserved_device;
1068             rctx.low_use_drive = dev;
1069             Dmsg2(110, "set low use drive=%s num_writers=%d\n", dev->print_name(),
1070                rctx.num_writers);
1071          } else {
1072             Dmsg1(110, "not low use num_writers=%d\n", dev->num_writers+ 
1073                dev->reserved_device);
1074          }
1075          Dmsg1(110, "failed: !prefMnt && busy. JobId=%u\n", jcr->JobId);
1076          Mmsg(jcr->errmsg, _("3605 JobId=%u wants free drive but device %s is busy.\n"), 
1077             jcr->JobId, dev->print_name());
1078          queue_reserve_message(jcr);
1079          return 0;
1080       }
1081
1082       /* Check for prefer mounted volumes */
1083 //    if (rctx.PreferMountedVols && !dev->VolHdr.VolumeName[0] && dev->is_tape()) {
1084       if (rctx.PreferMountedVols && !dev->vol && dev->is_tape()) {
1085          Mmsg(jcr->errmsg, _("3606 JobId=%u prefers mounted drives, but drive %s has no Volume.\n"), 
1086             jcr->JobId, dev->print_name());
1087          queue_reserve_message(jcr);
1088          Dmsg1(110, "failed: want mounted -- no vol JobId=%u\n", (uint32_t)jcr->JobId);
1089          return 0;                 /* No volume mounted */
1090       }
1091
1092       /* Check for exact Volume name match */
1093       /*  ***FIXME***  use dev->vol.VolumeName */
1094       if (rctx.exact_match && rctx.have_volume &&
1095           strcmp(dev->VolHdr.VolumeName, rctx.VolumeName) != 0) {
1096          Mmsg(jcr->errmsg, _("3607 JobId=%u wants Vol=\"%s\" drive has Vol=\"%s\" on drive %s.\n"), 
1097             jcr->JobId, rctx.VolumeName, dev->VolHdr.VolumeName, 
1098             dev->print_name());
1099          queue_reserve_message(jcr);
1100          Dmsg2(110, "failed: Not exact match have=%s want=%s\n",
1101                dev->VolHdr.VolumeName, rctx.VolumeName);
1102          return 0;
1103       }
1104    }
1105
1106    /* Check for unused autochanger drive */
1107    /* ***FIXME*** use !dev->is_busy() */
1108    if (rctx.autochanger_only && dev->num_writers == 0 &&
1109        dev->VolHdr.VolumeName[0] == 0) {
1110       /* Device is available but not yet reserved, reserve it for us */
1111       Dmsg2(100, "OK Res Unused autochanger %s JobId=%u.\n",
1112          dev->print_name(), jcr->JobId);
1113       bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
1114       bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
1115       return 1;                       /* reserve drive */
1116    }
1117
1118    /*
1119     * Handle the case that there are no writers
1120     */
1121    if (dev->num_writers == 0) {
1122       /* Now check if there are any reservations on the drive */
1123       if (dev->reserved_device) {           
1124          /* Now check if we want the same Pool and pool type */
1125          if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
1126              strcmp(dev->pool_type, dcr->pool_type) == 0) {
1127             /* OK, compatible device */
1128             Dmsg2(100, "OK dev: %s num_writers=0, reserved, pool matches JobId=%u\n",
1129                dev->print_name(), jcr->JobId);
1130             return 1;
1131          } else {
1132             /* Drive Pool not suitable for us */
1133             Mmsg(jcr->errmsg, _(
1134 "3608 JobId=%u wants Pool=\"%s\" but have Pool=\"%s\" nreserve=%d on drive %s.\n"), 
1135                   jcr->JobId, dcr->pool_name, dev->pool_name,
1136                   dev->reserved_device, dev->print_name());
1137             queue_reserve_message(jcr);
1138             Dmsg2(110, "failed: busy num_writers=0, reserved, pool=%s wanted=%s\n",
1139                dev->pool_name, dcr->pool_name);
1140             return 0;                 /* wait */
1141          }
1142       } else if (dev->can_append()) {
1143          /* Device in append mode, check if changing pool */
1144          if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
1145              strcmp(dev->pool_type, dcr->pool_type) == 0) {
1146             Dmsg2(100, "OK dev: %s num_writers=0, can_append, pool matches. JobId=%u\n",
1147                dev->print_name(), jcr->JobId);
1148             /* OK, compatible device */
1149             return 1;
1150          } else {
1151             /* Changing pool, unload old tape if any in drive */
1152             Dmsg0(100, "OK dev: num_writers=0, not reserved, pool change, unload changer\n");
1153             unload_autochanger(dcr, 0);
1154          }
1155       }
1156       /* Device is available but not yet reserved, reserve it for us */
1157       Dmsg2(100, "OK Dev avail reserved %s JobId=%u\n", dev->print_name(),
1158          jcr->JobId);
1159       bstrncpy(dev->pool_name, dcr->pool_name, sizeof(dev->pool_name));
1160       bstrncpy(dev->pool_type, dcr->pool_type, sizeof(dev->pool_type));
1161       return 1;                       /* reserve drive */
1162    }
1163
1164    /*
1165     * Check if the device is in append mode with writers (i.e.
1166     *  available if pool is the same).
1167     */
1168    if (dev->can_append() || dev->num_writers > 0) {
1169       /* Yes, now check if we want the same Pool and pool type */
1170       if (strcmp(dev->pool_name, dcr->pool_name) == 0 &&
1171           strcmp(dev->pool_type, dcr->pool_type) == 0) {
1172          Dmsg2(100, "OK dev: %s num_writers>=0, can_append, pool matches. JobId=%u\n",
1173             dev->print_name(), jcr->JobId);
1174          /* OK, compatible device */
1175          return 1;
1176       } else {
1177          /* Drive Pool not suitable for us */
1178          Mmsg(jcr->errmsg, _("3609 JobId=%u wants Pool=\"%s\" but has Pool=\"%s\" on drive %s.\n"), 
1179                jcr->JobId, dcr->pool_name, dev->pool_name, dev->print_name());
1180          queue_reserve_message(jcr);
1181          Dmsg2(110, "failed: busy num_writers>0, can_append, pool=%s wanted=%s\n",
1182             dev->pool_name, dcr->pool_name);
1183          return 0;                    /* wait */
1184       }
1185    } else {
1186       Pmsg0(000, _("Logic error!!!! Should not get here.\n"));
1187       Mmsg(jcr->errmsg, _("3910 JobId=%u Logic error!!!! drive %s Should not get here.\n"),
1188             jcr->JobId, dev->print_name());
1189       queue_reserve_message(jcr);
1190       Jmsg0(jcr, M_FATAL, 0, _("Logic error!!!! Should not get here.\n"));
1191       return -1;                      /* error, should not get here */
1192    }
1193    Mmsg(jcr->errmsg, _("3911 JobId=%u failed reserve drive %s.\n"), 
1194          jcr->JobId, dev->print_name());
1195    queue_reserve_message(jcr);
1196    Dmsg2(110, "failed: No reserve %s JobId=%u\n", dev->print_name(), jcr->JobId);
1197    return 0;
1198 }
1199
1200 /*
1201  * search_lock is already set on entering this routine 
1202  */
1203 static void queue_reserve_message(JCR *jcr)
1204 {
1205    int i;   
1206    alist *msgs = jcr->reserve_msgs;
1207    char *msg;
1208
1209    if (!msgs) {
1210       return;
1211    }
1212    /*
1213     * Look for duplicate message.  If found, do
1214     * not insert
1215     */
1216    for (i=msgs->size()-1; i >= 0; i--) {
1217       msg = (char *)msgs->get(i);
1218       if (!msg) {
1219          return;
1220       }
1221       /* Comparison based on 4 digit message number */
1222       if (strncmp(msg, jcr->errmsg, 4) == 0) {
1223          return;
1224       }
1225    }      
1226    /* Message unique, so insert it */
1227    jcr->reserve_msgs->push(bstrdup(jcr->errmsg));
1228 }
1229
1230 /*
1231  * Send any reservation messages queued for this jcr
1232  */
1233 void send_drive_reserve_messages(JCR *jcr, void sendit(const char *msg, int len, void *sarg), void *arg)
1234 {
1235    int i;
1236    alist *msgs;
1237    char *msg;
1238
1239    lock_reservations();
1240    msgs = jcr->reserve_msgs;
1241    if (!msgs || msgs->size() == 0) {
1242       unlock_reservations();
1243       return;
1244    }
1245    for (i=msgs->size()-1; i >= 0; i--) {
1246       msg = (char *)msgs->get(i);
1247       if (msg) {
1248          sendit("   ", 3, arg);
1249          sendit(msg, strlen(msg), arg);
1250       } else {
1251          break;
1252       }
1253    }
1254    unlock_reservations();
1255 }