]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/wait.c
This commit was manufactured by cvs2svn to create tag
[bacula/bacula] / bacula / src / stored / wait.c
1 /*
2  *  Subroutines to handle waiting for operator intervention
3  *   or waiting for a Device to be released
4  *
5  *  Code for wait_for_sysop() pulled from askdir.c
6  *
7  *   Kern Sibbald, March 2005
8  *
9  *   Version $Id$
10  */
11 /*
12    Copyright (C) 2000-2005 Kern Sibbald
13
14    This program is free software; you can redistribute it and/or
15    modify it under the terms of the GNU General Public License
16    version 2 as amended with additional clauses defined in the
17    file LICENSE in the main source directory.
18
19    This program is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
22    the file LICENSE for additional details.
23
24  */
25
26 #include "bacula.h"                   /* pull in global headers */
27 #include "stored.h"                   /* pull in Storage Deamon headers */
28
29 //static bool double_jcr_wait_time(JCR *jcr);
30
31
32 /*
33  * Wait for SysOp to mount a tape on a specific device
34  *
35  *   Returns: W_ERROR, W_TIMEOUT, W_POLL, W_MOUNT, or W_WAKE 
36  */
37 int wait_for_sysop(DCR *dcr)
38 {
39    struct timeval tv;
40    struct timezone tz;
41    struct timespec timeout;
42    time_t last_heartbeat = 0;
43    time_t first_start = time(NULL);
44    int stat = 0;
45    int add_wait;
46    bool unmounted;
47    DEVICE *dev = dcr->dev;
48    JCR *jcr = dcr->jcr;
49
50    P(dev->mutex);
51    Dmsg1(100, "Enter blocked=%s\n", dev->print_blocked());
52    unmounted = is_device_unmounted(dev);
53
54    dev->poll = false;
55    /*
56     * Wait requested time (dev->rem_wait_sec).  However, we also wake up every
57     *    HB_TIME seconds and send a heartbeat to the FD and the Director
58     *    to keep stateful firewalls from closing them down while waiting
59     *    for the operator.
60     */
61    add_wait = dev->rem_wait_sec;
62    if (me->heartbeat_interval && add_wait > me->heartbeat_interval) {
63       add_wait = me->heartbeat_interval;
64    }
65    /* If the user did not unmount the tape and we are polling, ensure
66     *  that we poll at the correct interval.
67     */
68    if (!unmounted && dev->vol_poll_interval && add_wait > dev->vol_poll_interval) {
69       add_wait = dev->vol_poll_interval;
70    }
71
72    if (!unmounted) {
73       Dmsg1(400, "blocked=%s\n", dev->print_blocked());
74       dev->dev_prev_blocked = dev->dev_blocked;
75       dev->set_blocked(BST_WAITING_FOR_SYSOP); /* indicate waiting for mount */
76    }
77
78    for ( ; !job_canceled(jcr); ) {
79       time_t now, start;
80
81       gettimeofday(&tv, &tz);
82       timeout.tv_nsec = tv.tv_usec * 1000;
83       timeout.tv_sec = tv.tv_sec + add_wait;
84
85       Dmsg4(400, "I'm going to sleep on device %s. HB=%d wait=%d add_wait=%d\n", 
86          dev->print_name(), (int)me->heartbeat_interval, dev->wait_sec, add_wait);
87       start = time(NULL);
88       /* Wait required time */
89       stat = pthread_cond_timedwait(&dev->wait_next_vol, &dev->mutex, &timeout);
90       Dmsg2(400, "Wokeup from sleep on device stat=%d blocked=%s\n", stat,
91          dev->print_blocked());
92
93       now = time(NULL);
94       dev->rem_wait_sec -= (now - start);
95
96       /* Note, this always triggers the first time. We want that. */
97       if (me->heartbeat_interval) {
98          if (now - last_heartbeat >= me->heartbeat_interval) {
99             /* send heartbeats */
100             if (jcr->file_bsock) {
101                bnet_sig(jcr->file_bsock, BNET_HEARTBEAT);
102                Dmsg0(400, "Send heartbeat to FD.\n");
103             }
104             if (jcr->dir_bsock) {
105                bnet_sig(jcr->dir_bsock, BNET_HEARTBEAT);
106             }
107             last_heartbeat = now;
108          }
109       }
110
111       if (stat == EINVAL) {
112          berrno be;
113          Dmsg1(000, "pthread stat=%d\n", stat);
114          Jmsg1(jcr, M_FATAL, 0, _("pthread timedwait error. ERR=%s\n"), be.strerror(stat));
115          stat = W_ERROR;               /* error */
116          break;
117       }
118
119
120       if (dev->rem_wait_sec <= 0) {  /* on exceeding wait time return */
121          Dmsg0(400, "Exceed wait time.\n");
122          stat = W_TIMEOUT;
123          break;
124       }
125
126       /*
127        * Check if user unmounted the device while we were waiting
128        */
129       unmounted = is_device_unmounted(dev);
130
131       if (!unmounted && dev->vol_poll_interval &&
132           (now - first_start >= dev->vol_poll_interval)) {
133          Dmsg1(400, "In wait blocked=%s\n", dev->print_blocked());
134          dev->poll = true;            /* returning a poll event */
135          stat = W_POLL;
136          break;
137       }
138       /*
139        * Check if user mounted the device while we were waiting
140        */
141       if (dev->get_blocked() == BST_MOUNT) {   /* mount request ? */
142          stat = W_MOUNT;
143          break;
144       }
145
146       /*
147        * If we did not timeout, then some event happened, so
148        *   return to check if state changed.   
149        */
150       if (stat != 0) {
151          stat = W_WAKE;          /* someone woke us */
152          break;
153       }
154
155       /* 
156        * At this point, we know we woke up because of a timeout,
157        *   that was due to a heartbeat, so we just update
158        *   the wait counters and continue.
159        */
160       add_wait = dev->wait_sec - (now - start);
161       if (add_wait < 0) {
162          add_wait = 0;
163       }
164       if (me->heartbeat_interval && add_wait > me->heartbeat_interval) {
165          add_wait = me->heartbeat_interval;
166       }
167    }
168
169    if (!unmounted) {
170       dev->set_blocked(dev->dev_prev_blocked);    /* restore entry state */
171       Dmsg1(400, "set %s\n", dev->print_blocked());
172    }
173    Dmsg1(400, "Exit blocked=%s\n", dev->print_blocked());
174    V(dev->mutex);
175    return stat;
176 }
177
178
179 /*
180  * Wait for any device to be released, then we return, so 
181  *  higher level code can rescan possible devices.
182  * 
183  * Returns: true  if a device has changed state
184  *          false if the total wait time has expired.
185  */
186 bool wait_for_device(JCR *jcr, bool first)
187 {
188    struct timeval tv;
189    struct timezone tz;
190    struct timespec timeout;
191    int stat = 0;
192    bool ok = true;
193    const int wait_time = 5 * 60;        /* wait 5 minutes */
194
195    Dmsg0(100, "Enter wait_for_device\n");
196    P(device_release_mutex);
197
198    if (first) {
199       Jmsg(jcr, M_MOUNT, 0, _("Job %s waiting to reserve a device.\n"), jcr->Job);
200    }
201
202    gettimeofday(&tv, &tz);
203    timeout.tv_nsec = tv.tv_usec * 1000;
204    timeout.tv_sec = tv.tv_sec + wait_time;
205
206    Dmsg0(100, "I'm going to wait for a device.\n");
207
208    /* Wait required time */
209    stat = pthread_cond_timedwait(&wait_device_release, &device_release_mutex, &timeout);
210    Dmsg1(100, "Wokeup from sleep on device stat=%d\n", stat);
211
212
213    V(device_release_mutex);
214    Dmsg1(100, "Return from wait_device ok=%d\n", ok);
215    return ok;
216 }
217
218 #ifdef xxx
219 /*
220  * The jcr timers are used for waiting on any device *
221  * Returns: true if time doubled
222  *          false if max time expired
223  */
224 static bool double_jcr_wait_time(JCR *jcr)
225 {
226    jcr->wait_sec *= 2;               /* double wait time */
227    if (jcr->wait_sec > jcr->max_wait) {   /* but not longer than maxtime */
228       jcr->wait_sec = jcr->max_wait;
229    }
230    jcr->num_wait++;
231    jcr->rem_wait_sec = jcr->wait_sec;
232    if (jcr->num_wait >= jcr->max_num_wait) {
233       return false;
234    }
235    return true;
236 }
237 #endif