]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/askdir.c
Integrate Nicolas' patch for direct DVD support.
[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 as
14    published by the Free Software Foundation; either version 2 of
15    the License, or (at your option) any later version.
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 GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public
23    License along with this program; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"                   /* pull in global headers */
30 #include "stored.h"                   /* pull in Storage Deamon headers */
31
32 /* Requests sent to the Director */
33 static char Find_media[]   = "CatReq Job=%s FindMedia=%d\n";
34 static char Get_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s write=%d\n";
35 static char Update_media[] = "CatReq Job=%s UpdateMedia VolName=%s"
36    " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%s VolMounts=%u"
37    " VolErrors=%u VolWrites=%u MaxVolBytes=%s EndTime=%d VolStatus=%s"
38    " Slot=%d relabel=%d InChanger=%d VolReadTime=%s VolWriteTime=%s"
39    " VolParts=%u\n";
40 static char Create_job_media[] = "CatReq Job=%s CreateJobMedia"
41    " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u"
42    " StartBlock=%u EndBlock=%u\n";
43 static char FileAttributes[] = "UpdCat Job=%s FileAttributes ";
44 static char Job_status[]     = "3012 Job %s jobstatus %d\n";
45
46
47 /* Responses received from the Director */
48 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%u"
49    " VolBlocks=%u VolBytes=%" lld " VolMounts=%u VolErrors=%u VolWrites=%u"
50    " MaxVolBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s"
51    " Slot=%d MaxVolJobs=%u MaxVolFiles=%u InChanger=%d"
52    " VolReadTime=%" lld " VolWriteTime=%" lld " EndFile=%u EndBlock=%u VolParts=%u";
53
54
55 static char OK_create[] = "1000 OK CreateJobMedia\n";
56
57 /* Forward referenced functions */
58 static int wait_for_sysop(DCR *dcr);
59
60 /*
61  * Send current JobStatus to Director
62  */
63 bool dir_send_job_status(JCR *jcr)
64 {
65    return bnet_fsend(jcr->dir_bsock, Job_status, jcr->Job, jcr->JobStatus);
66 }
67
68 /*
69  * Common routine for:
70  *   dir_get_volume_info()
71  * and
72  *   dir_find_next_appendable_volume()
73  *
74  *  Returns: true  on success and vol info in dcr->VolCatInfo
75  *           false on failure
76  */
77 static bool do_get_volume_info(DCR *dcr)
78 {
79     JCR *jcr = dcr->jcr;
80     BSOCK *dir = jcr->dir_bsock;
81     VOLUME_CAT_INFO vol;
82     int n;
83     int InChanger;
84
85     dcr->VolumeName[0] = 0;           /* No volume */
86     if (bnet_recv(dir) <= 0) {
87        Dmsg0(200, "getvolname error bnet_recv\n");
88        Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
89        return false;
90     }
91     memset(&vol, 0, sizeof(vol));
92     Dmsg1(300, "Get vol info=%s", dir->msg);
93     n = sscanf(dir->msg, OK_media, vol.VolCatName,
94                &vol.VolCatJobs, &vol.VolCatFiles,
95                &vol.VolCatBlocks, &vol.VolCatBytes,
96                &vol.VolCatMounts, &vol.VolCatErrors,
97                &vol.VolCatWrites, &vol.VolCatMaxBytes,
98                &vol.VolCatCapacityBytes, vol.VolCatStatus,
99                &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
100                &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
101                &vol.EndFile, &vol.EndBlock, &vol.VolCatParts);
102     if (n != 20) {
103        Dmsg2(100, "Bad response from Dir fields=%d: %s\n", n, dir->msg);
104        Mmsg(jcr->errmsg, _("Error getting Volume info: %s\n"), dir->msg);
105        return false;
106     }
107     vol.InChanger = InChanger;        /* bool in structure */
108     unbash_spaces(vol.VolCatName);
109     bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
110     memcpy(&dcr->VolCatInfo, &vol, sizeof(dcr->VolCatInfo));
111
112     Dmsg2(300, "do_reqest_vol_info got slot=%d Volume=%s\n",
113           vol.Slot, vol.VolCatName);
114     return true;
115 }
116
117
118 /*
119  * Get Volume info for a specific volume from the Director's Database
120  *
121  * Returns: true  on success   (not Director guarantees that Pool and MediaType
122  *                             are correct and VolStatus==Append or
123  *                             VolStatus==Recycle)
124  *          false on failure
125  *
126  *          Volume information returned in jcr
127  */
128 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
129 {
130     JCR *jcr = dcr->jcr;
131     BSOCK *dir = jcr->dir_bsock;
132
133     bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
134     Dmsg1(300, "dir_get_volume_info=%s\n", dcr->VolCatInfo.VolCatName);
135     bash_spaces(dcr->VolCatInfo.VolCatName);
136     bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
137        writing==GET_VOL_INFO_FOR_WRITE?1:0);
138     return do_get_volume_info(dcr);
139 }
140
141
142
143 /*
144  * Get info on the next appendable volume in the Director's database
145  * Returns: true  on success
146  *          false on failure
147  *
148  *          Volume information returned in dcr
149  *
150  */
151 bool dir_find_next_appendable_volume(DCR *dcr)
152 {
153     JCR *jcr = dcr->jcr;
154     BSOCK *dir = jcr->dir_bsock;
155     JCR *njcr;
156
157     Dmsg0(200, "dir_find_next_appendable_volume\n");
158     /*
159      * Try the three oldest or most available volumes.  Note,
160      *   the most available could already be mounted on another
161      *   drive, so we continue looking for a not in use Volume.
162      */
163     for (int vol_index=1;  vol_index < 3; vol_index++) {
164        bnet_fsend(dir, Find_media, jcr->Job, vol_index);
165        if (do_get_volume_info(dcr)) {
166           Dmsg2(300, "JobId=%d got possible Vol=%s\n", jcr->JobId, dcr->VolumeName);
167           bool found = false;
168           /*
169            * Walk through all jobs and see if the volume is
170            *  already mounted. If so, try a different one.
171            * This would be better done by walking through
172            *  all the devices.
173            */
174           lock_jcr_chain();
175           foreach_jcr(njcr) {
176              if (jcr == njcr) {
177                 free_locked_jcr(njcr);
178                 continue;             /* us */
179              }
180              Dmsg2(300, "Compare to JobId=%d using Vol=%s\n", njcr->JobId, njcr->dcr->VolumeName);
181              if (njcr->dcr && strcmp(dcr->VolumeName, njcr->dcr->VolumeName) == 0) {
182                 found = true;
183                 Dmsg1(400, "Vol in use by JobId=%u\n", njcr->JobId);
184                 free_locked_jcr(njcr);
185                 break;
186              }
187              free_locked_jcr(njcr);
188           }
189           unlock_jcr_chain();
190           if (!found) {
191              Dmsg0(400, "dir_find_next_appendable_volume return true\n");
192              return true;             /* Got good Volume */
193           }
194        } else {
195           Dmsg0(200, "No volume info, return false\n");
196           return false;
197        }
198     }
199     Dmsg0(400, "dir_find_next_appendable_volume return true\n");
200     return true;
201 }
202
203
204 /*
205  * After writing a Volume, send the updated statistics
206  * back to the director. The information comes from the
207  * dev record.
208  */
209 bool dir_update_volume_info(DCR *dcr, bool label)
210 {
211    JCR *jcr = dcr->jcr;
212    BSOCK *dir = jcr->dir_bsock;
213    DEVICE *dev = dcr->dev;
214    time_t LastWritten = time(NULL);
215    char ed1[50], ed2[50], ed3[50], ed4[50];
216    VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
217    int InChanger;
218
219    if (vol->VolCatName[0] == 0) {
220       Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
221       Dmsg0(000, "NULL Volume name. This shouldn't happen!!!\n");
222       return false;
223    }
224    if (dev_state(dev, ST_READ)) {
225       Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
226       Dmsg0(000, "Attempt to update_volume_info in read mode!!!\n");
227       return false;
228    }
229
230    Dmsg1(300, "Update cat VolFiles=%d\n", dev->file);
231    /* Just labeled or relabeled the tape */
232    if (label) {
233       bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
234       vol->VolCatBytes = 1;           /* indicates tape labeled */
235    }
236    bash_spaces(vol->VolCatName);
237    InChanger = vol->InChanger;
238    bnet_fsend(dir, Update_media, jcr->Job,
239       vol->VolCatName, vol->VolCatJobs, vol->VolCatFiles,
240       vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
241       vol->VolCatMounts, vol->VolCatErrors,
242       vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
243       LastWritten, vol->VolCatStatus, vol->Slot, label,
244       InChanger,                      /* bool in structure */
245       edit_uint64(vol->VolReadTime, ed3),
246       edit_uint64(vol->VolWriteTime, ed4),
247       vol->VolCatParts );
248
249    Dmsg1(300, "update_volume_info(): %s", dir->msg);
250    unbash_spaces(vol->VolCatName);
251
252    if (!do_get_volume_info(dcr)) {
253       Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
254       Dmsg1(000, "Didn't get vol info: %s", jcr->errmsg);
255       return false;
256    }
257    Dmsg1(420, "get_volume_info(): %s", dir->msg);
258    /* Update dev Volume info in case something changed (e.g. expired) */
259    memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->VolCatInfo));
260    return true;
261 }
262
263 /*
264  * After writing a Volume, create the JobMedia record.
265  */
266 bool dir_create_jobmedia_record(DCR *dcr)
267 {
268    JCR *jcr = dcr->jcr;
269    BSOCK *dir = jcr->dir_bsock;
270
271    if (!dcr->WroteVol) {
272       return true;                    /* nothing written to tape */
273    }
274
275    dcr->WroteVol = false;
276    bnet_fsend(dir, Create_job_media, jcr->Job,
277       dcr->VolFirstIndex, dcr->VolLastIndex,
278       dcr->StartFile, dcr->EndFile,
279       dcr->StartBlock, dcr->EndBlock);
280    Dmsg1(400, "create_jobmedia(): %s", dir->msg);
281    if (bnet_recv(dir) <= 0) {
282       Dmsg0(190, "create_jobmedia error bnet_recv\n");
283       Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
284            bnet_strerror(dir));
285       return false;
286    }
287    Dmsg1(400, "Create_jobmedia: %s", dir->msg);
288    if (strcmp(dir->msg, OK_create) != 0) {
289       Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
290       Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
291       return false;
292    }
293    return true;
294 }
295
296
297 /*
298  * Update File Attribute data
299  */
300 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
301 {
302    JCR *jcr = dcr->jcr;
303    BSOCK *dir = jcr->dir_bsock;
304    ser_declare;
305
306    dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
307    dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
308                 sizeof(DEV_RECORD) + rec->data_len);
309    ser_begin(dir->msg + dir->msglen, 0);
310    ser_uint32(rec->VolSessionId);
311    ser_uint32(rec->VolSessionTime);
312    ser_int32(rec->FileIndex);
313    ser_int32(rec->Stream);
314    ser_uint32(rec->data_len);
315    ser_bytes(rec->data, rec->data_len);
316    dir->msglen = ser_length(dir->msg);
317    return bnet_send(dir);
318 }
319
320
321 /*
322  *   Request the sysop to create an appendable volume
323  *
324  *   Entered with device blocked.
325  *   Leaves with device blocked.
326  *
327  *   Returns: true  on success (operator issues a mount command)
328  *            false on failure
329  *              Note, must create dev->errmsg on error return.
330  *
331  *    On success, dcr->VolumeName and dcr->VolCatInfo contain
332  *      information on suggested volume, but this may not be the
333  *      same as what is actually mounted.
334  *
335  *    When we return with success, the correct tape may or may not
336  *      actually be mounted. The calling routine must read it and
337  *      verify the label.
338  */
339 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
340 {
341    int stat = 0, jstat;
342    bool unmounted;
343    bool first = true;
344    DEVICE *dev = dcr->dev;
345    JCR *jcr = dcr->jcr;
346
347    Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
348    ASSERT(dev->dev_blocked);
349    for ( ;; ) {
350       if (job_canceled(jcr)) {
351          Mmsg(dev->errmsg,
352               _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
353               jcr->Job, dcr->dev_name);
354          Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
355          return false;
356       }
357       /* First pass, we *know* there are no appendable volumes, so no need to call */
358       if (!first && dir_find_next_appendable_volume(dcr)) { /* get suggested volume */
359          unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
360                      (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
361          /*
362           * If we have a valid volume name and we are not
363           *   removable media, return now, or if we have a
364           *   Slot for an autochanger, otherwise wait
365           *   for the operator to mount the media.
366           */
367          if (!unmounted && ((dcr->VolumeName[0] && !dev_cap(dev, CAP_REM) &&
368                 dev_cap(dev, CAP_LABEL)) ||
369                  (dcr->VolumeName[0] && dcr->VolCatInfo.Slot))) {
370             Dmsg0(400, "Return 1 from mount without wait.\n");
371             return true;
372          }
373          jstat = JS_WaitMount;
374          if (!dev->poll) {
375             Jmsg(jcr, M_MOUNT, 0, _(
376 "Please mount Volume \"%s\" on Storage Device \"%s\" for Job %s\n"
377 "Use \"mount\" command to release Job.\n"),
378               dcr->VolumeName, dcr->dev_name, jcr->Job);
379             Dmsg3(400, "Mount %s on %s for Job %s\n",
380                   dcr->VolumeName, dcr->dev_name, jcr->Job);
381          }
382       } else {
383          jstat = JS_WaitMedia;
384          if (!dev->poll) {
385             Jmsg(jcr, M_MOUNT, 0, _(
386 "Job %s waiting. Cannot find any appendable volumes.\n"
387 "Please use the \"label\"  command to create a new Volume for:\n"
388 "    Storage:      %s\n"
389 "    Media type:   %s\n"
390 "    Pool:         %s\n"),
391                jcr->Job,
392                dcr->dev_name,
393                dcr->media_type,
394                dcr->pool_name);
395          }
396       }
397       first = false;
398
399       jcr->JobStatus = jstat;
400       dir_send_job_status(jcr);
401
402       stat = wait_for_sysop(dcr);
403       if (dev->poll) {
404          Dmsg1(400, "Poll timeout in create append vol on device %s\n", dev_name(dev));
405          continue;
406       }
407
408       if (stat == ETIMEDOUT) {
409          if (!double_dev_wait_time(dev)) {
410             Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device \"%s\" for Job %s\n"),
411                dev_name(dev), jcr->Job);
412             Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
413             Dmsg1(400, "Gave up waiting on device %s\n", dev_name(dev));
414             return false;             /* exceeded maximum waits */
415          }
416          continue;
417       }
418       if (stat == EINVAL) {
419          berrno be;
420          Mmsg2(dev->errmsg, _("pthread error in mount_next_volume stat=%d ERR=%s\n"),
421                stat, be.strerror(stat));
422          Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
423          return false;
424       }
425       if (stat != 0) {
426          berrno be;
427          Jmsg(jcr, M_WARNING, 0, _("pthread error in mount_next_volume stat=%d ERR=%s\n"), stat,
428             be.strerror(stat));
429       }
430       Dmsg1(400, "Someone woke me for device %s\n", dev_name(dev));
431
432       /* If no VolumeName, and cannot get one, try again */
433       if (dcr->VolumeName[0] == 0 && !job_canceled(jcr) &&
434           !dir_find_next_appendable_volume(dcr)) {
435          Jmsg(jcr, M_MOUNT, 0, _(
436 "Someone woke me up, but I cannot find any appendable\n"
437 "volumes for Job=%s.\n"), jcr->Job);
438          /* Restart wait counters after user interaction */
439          init_dev_wait_timers(dev);
440          continue;
441       }
442       unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
443                   (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
444       if (unmounted) {
445          continue;                    /* continue to wait */
446       }
447
448       /*
449        * Device mounted, we have a volume, break and return
450        */
451       break;
452    }
453    set_jcr_job_status(jcr, JS_Running);
454    dir_send_job_status(jcr);
455    Dmsg0(400, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
456    return true;
457 }
458
459 /*
460  *   Request to mount specific Volume
461  *
462  *   Entered with device blocked and dcr->VolumeName is desired
463  *      volume.
464  *   Leaves with device blocked.
465  *
466  *   Returns: true  on success (operator issues a mount command)
467  *            false on failure
468  *                  Note, must create dev->errmsg on error return.
469  *
470  */
471 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
472 {
473    int stat = 0;
474    const char *msg;
475    DEVICE *dev = dcr->dev;
476    JCR *jcr = dcr->jcr;
477
478    Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
479    if (!dcr->VolumeName[0]) {
480       Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
481       return false;
482    }
483    ASSERT(dev->dev_blocked);
484    for ( ;; ) {
485       if (job_canceled(jcr)) {
486          Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
487               jcr->Job, dcr->dev_name);
488          return false;
489       }
490
491       if (!dev->poll) {
492          msg = _("Please mount");
493          Jmsg(jcr, M_MOUNT, 0, _("%s Volume \"%s\" on Storage Device \"%s\" for Job %s\n"),
494               msg, dcr->VolumeName, dcr->dev_name, jcr->Job);
495          Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
496                dcr->VolumeName, dcr->dev_name, jcr->Job);
497       }
498
499       jcr->JobStatus = JS_WaitMount;
500       dir_send_job_status(jcr);
501
502       stat = wait_for_sysop(dcr);    ;     /* wait on device */
503       if (dev->poll) {
504          Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev_name(dev));
505          Dmsg1(400, "Blocked=%s\n", edit_blocked_reason(dev));
506          return true;
507       }
508
509       if (stat == ETIMEDOUT) {
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_name(dev), jcr->Job);
513             Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
514             Dmsg1(400, "Gave up waiting on device %s\n", dev_name(dev));
515             return false;             /* exceeded maximum waits */
516          }
517          continue;
518       }
519       if (stat == EINVAL) {
520          berrno be;
521          Mmsg2(dev->errmsg, _("pthread error in mount_volume stat=%d ERR=%s\n"),
522                stat, be.strerror(stat));
523          Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
524          return false;
525       }
526       if (stat != 0) {
527          berrno be;
528          Jmsg(jcr, M_FATAL, 0, _("pthread error in mount_next_volume stat=%d ERR=%s\n"), stat,
529             be.strerror(stat));
530       }
531       Dmsg1(400, "Someone woke me for device %s\n", dev_name(dev));
532       break;
533    }
534    set_jcr_job_status(jcr, JS_Running);
535    dir_send_job_status(jcr);
536    Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");
537    return true;
538 }
539
540 /*
541  * Wait for SysOp to mount a tape
542  */
543 static int wait_for_sysop(DCR *dcr)
544 {
545    struct timeval tv;
546    struct timezone tz;
547    struct timespec timeout;
548    time_t last_heartbeat = 0;
549    time_t first_start = time(NULL);
550    int stat = 0;
551    int add_wait;
552    bool unmounted;
553    DEVICE *dev = dcr->dev;
554    JCR *jcr = dcr->jcr;
555
556    P(dev->mutex);
557    unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
558                 (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
559
560    dev->poll = false;
561    /*
562     * Wait requested time (dev->rem_wait_sec).  However, we also wake up every
563     *    HB_TIME seconds and send a heartbeat to the FD and the Director
564     *    to keep stateful firewalls from closing them down while waiting
565     *    for the operator.
566     */
567    add_wait = dev->rem_wait_sec;
568    if (me->heartbeat_interval && add_wait > me->heartbeat_interval) {
569       add_wait = me->heartbeat_interval;
570    }
571    /* If the user did not unmount the tape and we are polling, ensure
572     *  that we poll at the correct interval.
573     */
574    if (!unmounted && dev->vol_poll_interval && add_wait > dev->vol_poll_interval) {
575       add_wait = dev->vol_poll_interval;
576    }
577    gettimeofday(&tv, &tz);
578    timeout.tv_nsec = tv.tv_usec * 1000;
579    timeout.tv_sec = tv.tv_sec + add_wait;
580
581    if (!unmounted) {
582       dev->dev_prev_blocked = dev->dev_blocked;
583       dev->dev_blocked = BST_WAITING_FOR_SYSOP; /* indicate waiting for mount */
584    }
585
586    for ( ; !job_canceled(jcr); ) {
587       time_t now, start;
588
589       Dmsg3(400, "I'm going to sleep on device %s. HB=%d wait=%d\n", dev_name(dev),
590          (int)me->heartbeat_interval, dev->wait_sec);
591       start = time(NULL);
592       /* Wait required time */
593       stat = pthread_cond_timedwait(&dev->wait_next_vol, &dev->mutex, &timeout);
594       Dmsg1(400, "Wokeup from sleep on device stat=%d\n", stat);
595
596       now = time(NULL);
597       dev->rem_wait_sec -= (now - start);
598
599       /* Note, this always triggers the first time. We want that. */
600       if (me->heartbeat_interval) {
601          if (now - last_heartbeat >= me->heartbeat_interval) {
602             /* send heartbeats */
603             if (jcr->file_bsock) {
604                bnet_sig(jcr->file_bsock, BNET_HEARTBEAT);
605                Dmsg0(400, "Send heartbeat to FD.\n");
606             }
607             if (jcr->dir_bsock) {
608                bnet_sig(jcr->dir_bsock, BNET_HEARTBEAT);
609             }
610             last_heartbeat = now;
611          }
612       }
613
614       /*
615        * Check if user unmounted the device while we were waiting
616        */
617       unmounted = (dev->dev_blocked == BST_UNMOUNTED) ||
618                    (dev->dev_blocked == BST_UNMOUNTED_WAITING_FOR_SYSOP);
619
620       if (stat != ETIMEDOUT) {     /* we blocked the device */
621          break;                    /* on error return */
622       }
623       if (dev->rem_wait_sec <= 0) {  /* on exceeding wait time return */
624          Dmsg0(400, "Exceed wait time.\n");
625          break;
626       }
627
628       if (!unmounted && dev->vol_poll_interval &&
629           (now - first_start >= dev->vol_poll_interval)) {
630          Dmsg1(400, "In wait blocked=%s\n", edit_blocked_reason(dev));
631          dev->poll = true;            /* returning a poll event */
632          break;
633       }
634       /*
635        * Check if user mounted the device while we were waiting
636        */
637       if (dev->dev_blocked == BST_MOUNT) {   /* mount request ? */
638          stat = 0;
639          break;
640       }
641
642       add_wait = dev->wait_sec - (now - start);
643       if (add_wait < 0) {
644          add_wait = 0;
645       }
646       if (me->heartbeat_interval && add_wait > me->heartbeat_interval) {
647          add_wait = me->heartbeat_interval;
648       }
649       gettimeofday(&tv, &tz);
650       timeout.tv_nsec = tv.tv_usec * 1000;
651       timeout.tv_sec = tv.tv_sec + add_wait; /* additional wait */
652       Dmsg1(400, "Additional wait %d sec.\n", add_wait);
653    }
654
655    if (!unmounted) {
656       dev->dev_blocked = dev->dev_prev_blocked;    /* restore entry state */
657    }
658    V(dev->mutex);
659    return stat;
660 }