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