]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/askdir.c
kes Simplify locking in the reservations system.
[bacula/bacula] / bacula / src / stored / askdir.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  *  Subroutines to handle Catalog reqests sent to the Director
30  *   Reqests/commands from the Director are handled in dircmd.c
31  *
32  *   Kern Sibbald, December 2000
33  *
34  *   Version $Id$
35  */
36
37 #include "bacula.h"                   /* pull in global headers */
38 #include "stored.h"                   /* pull in Storage Deamon headers */
39
40 /* Requests sent to the Director */
41 static char Find_media[]   = "CatReq Job=%s FindMedia=%d pool_name=%s media_type=%s\n";
42 static char Get_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s write=%d\n";
43 static char Update_media[] = "CatReq Job=%s UpdateMedia VolName=%s"
44    " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%s VolMounts=%u"
45    " VolErrors=%u VolWrites=%u MaxVolBytes=%s EndTime=%d VolStatus=%s"
46    " Slot=%d relabel=%d InChanger=%d VolReadTime=%s VolWriteTime=%s"
47    " VolFirstWritten=%s VolParts=%u\n";
48 static char Create_job_media[] = "CatReq Job=%s CreateJobMedia"
49    " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u"
50    " StartBlock=%u EndBlock=%u Copy=%d Strip=%d MediaId=%s\n";
51 static char FileAttributes[] = "UpdCat Job=%s FileAttributes ";
52 static char Job_status[]     = "Status Job=%s JobStatus=%d\n";
53
54 /* Responses received from the Director */
55 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
56    " VolBlocks=%lu VolBytes=%lld VolMounts=%lu VolErrors=%lu VolWrites=%lu"
57    " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
58    " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
59    " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
60    " VolParts=%lu LabelType=%ld MediaId=%lld\n";
61
62
63 static char OK_create[] = "1000 OK CreateJobMedia\n";
64
65 static pthread_mutex_t vol_info_mutex = PTHREAD_MUTEX_INITIALIZER;
66
67 #ifdef needed
68
69 static char Device_update[] = "DevUpd Job=%s device=%s "
70    "append=%d read=%d num_writers=%d "
71    "open=%d labeled=%d offline=%d "
72    "reserved=%d max_writers=%d "
73    "autoselect=%d autochanger=%d "
74    "changer_name=%s media_type=%s volume_name=%s\n";
75
76
77 /* Send update information about a device to Director */
78 bool dir_update_device(JCR *jcr, DEVICE *dev)
79 {
80    BSOCK *dir = jcr->dir_bsock;
81    POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
82    DEVRES *device = dev->device;
83    bool ok;
84    
85    pm_strcpy(dev_name, device->hdr.name);
86    bash_spaces(dev_name);
87    if (dev->is_labeled()) {
88       pm_strcpy(VolumeName, dev->VolHdr.VolumeName);
89    } else {
90       pm_strcpy(VolumeName, "*");
91    }
92    bash_spaces(VolumeName);
93    pm_strcpy(MediaType, device->media_type);
94    bash_spaces(MediaType);
95    if (device->changer_res) {
96       pm_strcpy(ChangerName, device->changer_res->hdr.name);
97       bash_spaces(ChangerName);
98    } else {
99       pm_strcpy(ChangerName, "*");
100    }
101    ok =bnet_fsend(dir, Device_update, 
102       jcr->Job,
103       dev_name.c_str(),
104       dev->can_append()!=0,
105       dev->can_read()!=0, dev->num_writers, 
106       dev->is_open()!=0, dev->is_labeled()!=0,
107       dev->is_offline()!=0, dev->reserved_device, 
108       dev->is_tape()?100000:1,
109       dev->autoselect, 0, 
110       ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
111    Dmsg1(100, ">dird: %s\n", dir->msg);
112    return ok;
113 }
114
115 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
116 {
117    BSOCK *dir = jcr->dir_bsock;
118    POOL_MEM dev_name, MediaType;
119    DEVRES *device;
120    bool ok;
121
122    pm_strcpy(dev_name, changer->hdr.name);
123    bash_spaces(dev_name);
124    device = (DEVRES *)changer->device->first();
125    pm_strcpy(MediaType, device->media_type);
126    bash_spaces(MediaType);
127    /* This is mostly to indicate that we are here */
128    ok = bnet_fsend(dir, Device_update,
129       jcr->Job,
130       dev_name.c_str(),         /* Changer name */
131       0, 0, 0,                  /* append, read, num_writers */
132       0, 0, 0,                  /* is_open, is_labeled, offline */
133       0, 0,                     /* reserved, max_writers */
134       0,                        /* Autoselect */
135       changer->device->size(),  /* Number of devices */
136       "0",                      /* PoolId */
137       "*",                      /* ChangerName */
138       MediaType.c_str(),        /* MediaType */
139       "*");                     /* VolName */
140    Dmsg1(100, ">dird: %s\n", dir->msg);
141    return ok;
142 }
143 #endif
144
145
146 /*
147  * Send current JobStatus to Director
148  */
149 bool dir_send_job_status(JCR *jcr)
150 {
151    return bnet_fsend(jcr->dir_bsock, Job_status, jcr->Job, jcr->JobStatus);
152 }
153
154 /*
155  * Common routine for:
156  *   dir_get_volume_info()
157  * and
158  *   dir_find_next_appendable_volume()
159  *
160  *  NOTE!!! All calls to this routine must be protected by
161  *          locking vol_info_mutex before calling it so that
162  *          we don't have one thread modifying the parameters
163  *          and another reading them.
164  *
165  *  Returns: true  on success and vol info in dcr->VolCatInfo
166  *           false on failure
167  */
168 static bool do_get_volume_info(DCR *dcr)
169 {
170     JCR *jcr = dcr->jcr;
171     BSOCK *dir = jcr->dir_bsock;
172     VOLUME_CAT_INFO vol;
173     int n;
174     int32_t InChanger;
175
176     if (bnet_recv(dir) <= 0) {
177        Dmsg0(200, "getvolname error bnet_recv\n");
178        Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
179        return false;
180     }
181     memset(&vol, 0, sizeof(vol));
182     Dmsg1(100, "<dird %s", dir->msg);
183     n = sscanf(dir->msg, OK_media, vol.VolCatName,
184                &vol.VolCatJobs, &vol.VolCatFiles,
185                &vol.VolCatBlocks, &vol.VolCatBytes,
186                &vol.VolCatMounts, &vol.VolCatErrors,
187                &vol.VolCatWrites, &vol.VolCatMaxBytes,
188                &vol.VolCatCapacityBytes, vol.VolCatStatus,
189                &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
190                &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
191                &vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
192                &vol.LabelType, &vol.VolMediaId);
193     if (n != 22) {
194        Dmsg2(100, "Bad response from Dir fields=%d: %s", n, dir->msg);
195        Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
196        return false;
197     }
198     vol.InChanger = InChanger;        /* bool in structure */
199     unbash_spaces(vol.VolCatName);
200     bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
201     dcr->VolCatInfo = vol;            /* structure assignment */
202
203     Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n",
204           vol.Slot, vol.VolCatName);
205     return true;
206 }
207
208
209 /*
210  * Get Volume info for a specific volume from the Director's Database
211  *
212  * Returns: true  on success   (Director guarantees that Pool and MediaType
213  *                              are correct and VolStatus==Append or
214  *                              VolStatus==Recycle)
215  *          false on failure
216  *
217  *          Volume information returned in dcr->VolCatInfo
218  */
219 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
220 {
221     JCR *jcr = dcr->jcr;
222     BSOCK *dir = jcr->dir_bsock;
223
224     P(vol_info_mutex);
225     bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
226     bash_spaces(dcr->VolCatInfo.VolCatName);
227     bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
228        writing==GET_VOL_INFO_FOR_WRITE?1:0);
229     Dmsg1(100, ">dird: %s", dir->msg);
230     unbash_spaces(dcr->VolCatInfo.VolCatName);
231     bool ok = do_get_volume_info(dcr);
232     V(vol_info_mutex);
233     return ok;
234 }
235
236
237
238 /*
239  * Get info on the next appendable volume in the Director's database
240  *
241  * Returns: true  on success dcr->VolumeName is volume
242  *                reserve_volume() called on Volume name
243  *          false on failure dcr->VolumeName[0] == 0
244  *
245  *          Volume information returned in dcr
246  *
247  */
248 bool dir_find_next_appendable_volume(DCR *dcr)
249 {
250     JCR *jcr = dcr->jcr;
251     BSOCK *dir = jcr->dir_bsock;
252     bool found = false;
253
254     Dmsg0(200, "dir_find_next_appendable_volume\n");
255     /*
256      * Try the twenty oldest or most available volumes.  Note,
257      *   the most available could already be mounted on another
258      *   drive, so we continue looking for a not in use Volume.
259      */
260     lock_reservations();
261     P(vol_info_mutex);
262     for (int vol_index=1;  vol_index < 20; vol_index++) {
263        bash_spaces(dcr->media_type);
264        bash_spaces(dcr->pool_name);
265        bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
266        unbash_spaces(dcr->media_type);
267        unbash_spaces(dcr->pool_name);
268        Dmsg1(100, ">dird: %s", dir->msg);
269        bool ok = do_get_volume_info(dcr);
270        if (ok) {
271           if (!is_volume_in_use(dcr)) {
272              found = true;
273              break;
274           } else {
275              Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
276              continue;
277           }
278        } else {
279           Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
280              dcr->dev->print_name());
281           found = false;
282           break;
283        }
284     }
285     if (found) {
286        Dmsg0(400, "dir_find_next_appendable_volume return true\n");
287        if (reserve_volume(dcr, dcr->VolumeName) == 0) {
288           Dmsg2(100, "Could not reserve volume %s on %s\n", dcr->VolumeName,
289               dcr->dev->print_name());
290           goto bail_out;
291        }
292        V(vol_info_mutex);
293        unlock_reservations();
294        return true;
295     }
296
297 bail_out:
298     dcr->VolumeName[0] = 0;
299     V(vol_info_mutex);
300     unlock_reservations();
301     return false;
302 }
303
304
305 /*
306  * After writing a Volume, send the updated statistics
307  * back to the director. The information comes from the
308  * dev record.
309  */
310 bool dir_update_volume_info(DCR *dcr, bool label)
311 {
312    JCR *jcr = dcr->jcr;
313    BSOCK *dir = jcr->dir_bsock;
314    DEVICE *dev = dcr->dev;
315    time_t LastWritten = time(NULL);
316    VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
317    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
318    int InChanger;
319    bool ok = false;
320    POOL_MEM VolumeName;
321
322    /* If system job, do not update catalog */
323    if (jcr->JobType == JT_SYSTEM) {
324       return true;
325    }
326
327    if (vol->VolCatName[0] == 0) {
328       Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
329       Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
330       return false;
331    }
332
333    /* Lock during Volume update */
334    P(vol_info_mutex);
335    Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
336    /* Just labeled or relabeled the tape */
337    if (label) {
338       bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
339    }
340    pm_strcpy(VolumeName, vol->VolCatName);
341    bash_spaces(VolumeName);
342    InChanger = vol->InChanger;
343    bnet_fsend(dir, Update_media, jcr->Job,
344       VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
345       vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
346       vol->VolCatMounts, vol->VolCatErrors,
347       vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
348       LastWritten, vol->VolCatStatus, vol->Slot, label,
349       InChanger,                      /* bool in structure */
350       edit_int64(vol->VolReadTime, ed3),
351       edit_int64(vol->VolWriteTime, ed4),
352       edit_uint64(vol->VolFirstWritten, ed5),
353       vol->VolCatParts);
354     Dmsg1(100, ">dird: %s", dir->msg);
355
356    /* Do not lock device here because it may be locked from label */
357    if (!do_get_volume_info(dcr)) {
358       Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
359       Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"), 
360          vol->VolCatName, jcr->errmsg);
361       goto bail_out;
362    }
363    Dmsg1(420, "get_volume_info(): %s", dir->msg);
364    /* Update dev Volume info in case something changed (e.g. expired) */
365    dev->VolCatInfo = dcr->VolCatInfo;
366    ok = true;
367
368 bail_out:
369    V(vol_info_mutex);
370    return ok;
371 }
372
373 /*
374  * After writing a Volume, create the JobMedia record.
375  */
376 bool dir_create_jobmedia_record(DCR *dcr)
377 {
378    JCR *jcr = dcr->jcr;
379    BSOCK *dir = jcr->dir_bsock;
380    char ed1[50];
381
382    /* If system job, do not update catalog */
383    if (jcr->JobType == JT_SYSTEM) {
384       return true;
385    }
386
387    if (!dcr->WroteVol) {
388       return true;                    /* nothing written to tape */
389    }
390
391    dcr->WroteVol = false;
392    bnet_fsend(dir, Create_job_media, jcr->Job,
393       dcr->VolFirstIndex, dcr->VolLastIndex,
394       dcr->StartFile, dcr->EndFile,
395       dcr->StartBlock, dcr->EndBlock, 
396       dcr->Copy, dcr->Stripe, 
397       edit_uint64(dcr->dev->VolCatInfo.VolMediaId, ed1));
398     Dmsg1(100, ">dird: %s", dir->msg);
399    if (bnet_recv(dir) <= 0) {
400       Dmsg0(190, "create_jobmedia error bnet_recv\n");
401       Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
402            bnet_strerror(dir));
403       return false;
404    }
405    Dmsg1(100, "<dir: %s", dir->msg);
406    if (strcmp(dir->msg, OK_create) != 0) {
407       Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
408       Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
409       return false;
410    }
411    return true;
412 }
413
414
415 /*
416  * Update File Attribute data
417  */
418 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
419 {
420    JCR *jcr = dcr->jcr;
421    BSOCK *dir = jcr->dir_bsock;
422    ser_declare;
423
424 #ifdef NO_ATTRIBUTES_TEST
425    return true;
426 #endif
427
428    dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
429    dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
430                 sizeof(DEV_RECORD) + rec->data_len);
431    ser_begin(dir->msg + dir->msglen, 0);
432    ser_uint32(rec->VolSessionId);
433    ser_uint32(rec->VolSessionTime);
434    ser_int32(rec->FileIndex);
435    ser_int32(rec->Stream);
436    ser_uint32(rec->data_len);
437    ser_bytes(rec->data, rec->data_len);
438    dir->msglen = ser_length(dir->msg);
439    Dmsg1(1800, ">dird: %s\n", dir->msg);    /* Attributes */
440    return bnet_send(dir);
441 }
442
443
444 /*
445  *   Request the sysop to create an appendable volume
446  *
447  *   Entered with device blocked.
448  *   Leaves with device blocked.
449  *
450  *   Returns: true  on success (operator issues a mount command)
451  *            false on failure
452  *              Note, must create dev->errmsg on error return.
453  *
454  *    On success, dcr->VolumeName and dcr->VolCatInfo contain
455  *      information on suggested volume, but this may not be the
456  *      same as what is actually mounted.
457  *
458  *    When we return with success, the correct tape may or may not
459  *      actually be mounted. The calling routine must read it and
460  *      verify the label.
461  */
462 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
463 {
464    int stat = W_TIMEOUT;
465    DEVICE *dev = dcr->dev;
466    JCR *jcr = dcr->jcr;
467    bool got_vol = false;
468
469    Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
470    ASSERT(dev->blocked());
471    for ( ;; ) {
472       if (job_canceled(jcr)) {
473          Mmsg(dev->errmsg,
474               _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
475               jcr->Job, dev->print_name());
476          Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
477          return false;
478       }
479       dev->dlock();  
480       got_vol = dir_find_next_appendable_volume(dcr);   /* get suggested volume */
481       dev->dunlock();
482       if (got_vol) {
483          return true;
484       } else {
485          if (stat == W_TIMEOUT || stat == W_MOUNT) {
486             Jmsg(jcr, M_MOUNT, 0, _(
487 "Job %s waiting. Cannot find any appendable volumes.\n"
488 "Please use the \"label\"  command to create a new Volume for:\n"
489 "    Storage:      %s\n"
490 "    Media type:   %s\n"
491 "    Pool:         %s\n"),
492                jcr->Job,
493                dev->print_name(),
494                dcr->media_type,
495                dcr->pool_name);
496          }
497       }
498
499       set_jcr_job_status(jcr, JS_WaitMedia);
500       dir_send_job_status(jcr);
501
502       stat = wait_for_sysop(dcr);
503       Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
504       if (dev->poll) {
505          Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
506          continue;
507       }
508
509       if (stat == W_TIMEOUT) {
510          if (!double_dev_wait_time(dev)) {
511             Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
512                dev->print_name(), jcr->Job);
513             Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
514             Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
515             return false;             /* exceeded maximum waits */
516          }
517          continue;
518       }
519       if (stat == W_ERROR) {
520          berrno be;
521          Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
522          Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
523          return false;
524       }
525       Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
526    }
527    set_jcr_job_status(jcr, JS_Running);
528    dir_send_job_status(jcr);
529    Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
530    return true;
531 }
532
533 /*
534  *   Request to mount specific Volume
535  *
536  *   Entered with device blocked and dcr->VolumeName is desired
537  *      volume.
538  *   Leaves with device blocked.
539  *
540  *   Returns: true  on success (operator issues a mount command)
541  *            false on failure
542  *                  Note, must create dev->errmsg on error return.
543  *
544  */
545 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
546 {
547    int stat = W_TIMEOUT;
548    DEVICE *dev = dcr->dev;
549    JCR *jcr = dcr->jcr;
550
551    Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
552    if (!dcr->VolumeName[0]) {
553       Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
554       return false;
555    }
556    ASSERT(dev->blocked());
557    for ( ;; ) {
558       if (job_canceled(jcr)) {
559          Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
560               jcr->Job, dev->print_name());
561          return false;
562       }
563
564       if (dev->is_dvd()) {   
565          dev->unmount(0);
566       }
567       
568       /*
569        * If we are not polling, and the wait timeout or the
570        *   user explicitly did a mount, send him the message.
571        *   Otherwise skip it.
572        */
573       if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
574          Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
575               dcr->VolumeName, dev->print_name(), jcr->Job);
576          Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
577                dcr->VolumeName, dev->print_name(), jcr->Job);
578       }
579
580       set_jcr_job_status(jcr, JS_WaitMount);
581       dir_send_job_status(jcr);
582
583       stat = wait_for_sysop(dcr);          /* wait on device */
584       Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
585       if (dev->poll) {
586          Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
587          Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
588          return true;
589       }
590
591       if (stat == W_TIMEOUT) {
592          if (!double_dev_wait_time(dev)) {
593             Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
594                dev->print_name(), jcr->Job);
595             Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
596             Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
597             return false;             /* exceeded maximum waits */
598          }
599          continue;
600       }
601       if (stat == W_ERROR) {
602          berrno be;
603          Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
604          Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
605          return false;
606       }
607       Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
608       break;
609    }
610    set_jcr_job_status(jcr, JS_Running);
611    dir_send_job_status(jcr);
612    Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");
613    return true;
614 }