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