]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/askdir.c
This commit was manufactured by cvs2svn to create tag
[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  */
8 /*
9    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License as
13    published by the Free Software Foundation; either version 2 of
14    the License, or (at your option) any later version.
15
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19    General Public License for more details.
20
21    You should have received a copy of the GNU General Public
22    License along with this program; if not, write to the Free
23    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24    MA 02111-1307, USA.
25
26  */
27
28 #include "bacula.h"                   /* pull in global headers */
29 #include "stored.h"                   /* pull in Storage Deamon headers */
30
31 /* Requests sent to the Director */
32 static char Find_media[]    = "CatReq Job=%s FindMedia=%d\n";
33 static char Get_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s\n";
34
35 static char Update_media[] = "CatReq Job=%s UpdateMedia VolName=%s\
36  VolJobs=%d VolFiles=%d VolBlocks=%d VolBytes=%" lld " VolMounts=%d\
37  VolErrors=%d VolWrites=%d VolMaxBytes=%" lld " EndTime=%d VolStatus=%s\
38  FirstIndex=%d LastIndex=%d StartFile=%d EndFile=%d \
39  StartBlock=%d EndBlock=%d\n";
40
41 static char FileAttributes[] = "UpdCat Job=%s FileAttributes ";
42
43 static char Job_status[]   = "3012 Job %s jobstatus %d\n";
44
45
46 /* Responses received from the Director */
47 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%d VolFiles=%d\
48  VolBlocks=%d VolBytes=%" lld " VolMounts=%d VolErrors=%d VolWrites=%d\
49  VolMaxBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%20s\n";
50
51 static char OK_update[] = "1000 OK UpdateMedia\n";
52
53
54 /*
55  * Send current JobStatus to Director
56  */
57 int dir_send_job_status(JCR *jcr)
58 {
59    return bnet_fsend(jcr->dir_bsock, Job_status, jcr->Job, jcr->JobStatus);
60 }
61
62 /*
63  * Common routine for:
64  *   dir_get_volume_info()
65  * and
66  *   dir_find_next_appendable_volume()
67  */
68 static int do_request_volume_info(JCR *jcr)
69 {
70     BSOCK *dir = jcr->dir_bsock;
71     VOLUME_CAT_INFO *vol = &jcr->VolCatInfo;
72
73     jcr->VolumeName[0] = 0;           /* No volume */
74     if (bnet_recv(dir) <= 0) {
75        Dmsg0(30, "getvolname error bnet_recv\n");
76        return 0;
77     }
78     if (sscanf(dir->msg, OK_media, vol->VolCatName, 
79                &vol->VolCatJobs, &vol->VolCatFiles,
80                &vol->VolCatBlocks, &vol->VolCatBytes, 
81                &vol->VolCatMounts, &vol->VolCatErrors,
82                &vol->VolCatWrites, &vol->VolCatMaxBytes, 
83                &vol->VolCatCapacityBytes, vol->VolCatStatus) != 11) {
84        Dmsg1(30, "Bad response from Dir: %s\n", dir->msg);
85        return 0;
86     }
87     unbash_spaces(vol->VolCatName);
88     strcpy(jcr->VolumeName, vol->VolCatName); /* set desired VolumeName */
89     
90     Dmsg1(200, "Got Volume=%s\n", vol->VolCatName);
91     return 1;
92 }
93
94
95 /*
96  * Get Volume info for a specific volume from the Director's Database
97  *
98  * Returns: 1 on success   (not Director guarantees that Pool and MediaType
99  *                          are correct and VolStatus==Append or
100  *                          VolStatus==Recycle)
101  *          0 on failure
102  *
103  *          Volume information returned in jcr
104  */
105 int dir_get_volume_info(JCR *jcr)
106 {
107     BSOCK *dir = jcr->dir_bsock;
108
109     strcpy(jcr->VolCatInfo.VolCatName, jcr->VolumeName);
110     Dmsg1(200, "dir_get_volume_info=%s\n", jcr->VolCatInfo.VolCatName);
111     bash_spaces(jcr->VolCatInfo.VolCatName);
112     bnet_fsend(dir, Get_Vol_Info, jcr->Job, jcr->VolCatInfo.VolCatName);
113     return do_request_volume_info(jcr);
114 }
115
116
117
118 /*
119  * Get info on the next appendable volume in the Director's database
120  * Returns: 1 on success
121  *          0 on failure
122  *
123  *          Volume information returned in jcr
124  *
125  */
126 int dir_find_next_appendable_volume(JCR *jcr)
127 {
128     BSOCK *dir = jcr->dir_bsock;
129
130     Dmsg0(200, "dir_find_next_appendable_volume\n");
131     bnet_fsend(dir, Find_media, jcr->Job, 1);
132     return do_request_volume_info(jcr);
133 }
134
135     
136 /*
137  * After writing a Volume, send the updated statistics
138  * back to the director.
139  */
140 int dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol)
141 {
142    BSOCK *dir = jcr->dir_bsock;
143    time_t EndTime = time(NULL);
144
145    if (vol->VolCatName[0] == 0) {
146       Emsg0(M_ERROR, 0, "NULL Volume name. This shouldn't happen!!!\n");
147    }
148    bnet_fsend(dir, Update_media, jcr->Job, 
149       vol->VolCatName, vol->VolCatJobs, vol->VolCatFiles,
150       vol->VolCatBlocks, vol->VolCatBytes, 
151       vol->VolCatMounts, vol->VolCatErrors,
152       vol->VolCatWrites, vol->VolCatMaxBytes, EndTime, 
153       vol->VolCatStatus, 
154       jcr->VolFirstFile, jcr->JobFiles,
155       jcr->start_file, jcr->end_file,
156       jcr->start_block, jcr->end_block);
157    Dmsg1(20, "update_volume_data(): %s", dir->msg);
158    if (bnet_recv(dir) <= 0) {
159       Dmsg0(90, "updateVolCatInfo error bnet_recv\n");
160       return 0;
161    }
162    Dmsg1(20, "Updatevol: %s", dir->msg);
163    if (strcmp(dir->msg, OK_update) != 0) {
164       Dmsg1(30, "Bad response from Dir: %s\n", dir->msg);
165       Jmsg(jcr, M_ERROR, 0, "Error updating Volume Info: %s\n", 
166          dir->msg);
167       return 0;
168    }
169    return 1;
170 }
171
172 /* 
173  * Update File Attribute data
174  */
175 int dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec)
176 {
177    BSOCK *dir = jcr->dir_bsock;
178    ser_declare;
179
180    dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
181    dir->msg = (char *) check_pool_memory_size(dir->msg, dir->msglen + 
182                 sizeof(DEV_RECORD) + rec->data_len);
183    ser_begin(dir->msg + dir->msglen, 0);
184    ser_uint32(rec->VolSessionId);
185    ser_uint32(rec->VolSessionTime);
186    ser_int32(rec->FileIndex);
187    ser_int32(rec->Stream);
188    ser_uint32(rec->data_len);
189    ser_bytes(rec->data, rec->data_len);
190    dir->msglen = ser_length(dir->msg);
191    return bnet_send(dir);
192 }
193
194
195 /*
196  *   
197  *   Entered with device blocked.
198  *   Leaves with device blocked.
199  *
200  *   Returns: 1 on success (operator issues a mount command)
201  *            0 on failure
202  *              Note, must create dev->errmsg on error return.
203  *
204  *    On success, jcr->VolumeName and jcr->VolCatInfo contain
205  *      information on suggested volume, but this may not be the
206  *      same as what is actually mounted.
207  */
208 int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev)
209 {
210    struct timeval tv;
211    struct timezone tz;
212    struct timespec timeout;
213    int stat, jstat;
214    /* ******FIXME******* put these on config variable */
215    int min_wait = 60 * 60;
216    int max_wait = 24 * 60 * 60;
217    int max_num_wait = 9;              /* 5 waits =~ 1 day, then 1 day at a time */
218
219    int wait_sec;
220    int num_wait = 0;
221    int dev_blocked;
222    char *msg;
223
224    Dmsg0(30, "enter dir_ask_sysop_to_mount_next_volume\n");
225    ASSERT(dev->dev_blocked);
226    wait_sec = min_wait;
227    for ( ;; ) {
228       if (job_cancelled(jcr)) {
229          Mmsg(&dev->errmsg, "Job %s cancelled while waiting for mount on Storage Device \"%s\".\n", 
230               jcr->Job, jcr->dev_name);
231          return 0;
232       }
233       if (dir_find_next_appendable_volume(jcr)) {    /* get suggested volume */
234          jstat = JS_WaitMount;
235          /*
236           * If we have a valid volume name and we are not
237           * removable media, return now, otherwise wait
238           * for the operator to mount the media.
239           */
240          if (jcr->VolumeName[0] && !(dev->capabilities & CAP_REM) &&      
241               dev->capabilities & CAP_LABEL) {
242             Dmsg0(90, "Return 1 from mount without wait.\n");
243             return 1;
244          }
245          if (dev->capabilities & CAP_ANONVOLS) {
246             msg = "Suggest mounting";
247          } else {
248             msg = "Please mount";
249          }
250          Jmsg(jcr, M_MOUNT, 0, "%s Volume \"%s\" on Storage Device \"%s\" for Job %s\n",
251               msg, jcr->VolumeName, jcr->dev_name, jcr->Job);
252          Dmsg3(90, "Mount %s on %s for Job %s\n",
253                 jcr->VolumeName, jcr->dev_name, jcr->Job);
254       } else {
255          jstat = JS_WaitMedia;
256          Jmsg(jcr, M_MOUNT, 0, "Job %s waiting. Cannot find any appendable volumes.\n\
257 Please use the \"unmount\" and \"label\"  commands to create new Volumes for:\n\
258    Storage Device \"%s\" with Pool \"%s\" and Media type \"%s\".\n\
259 Use \"mount\" to resume the job.\n",
260               jcr->Job, jcr->dev_name, jcr->pool_name, jcr->media_type);
261       }
262       /*
263        * Wait then send message again
264        */
265       gettimeofday(&tv, &tz);
266       timeout.tv_nsec = tv.tv_usec * 1000;
267       timeout.tv_sec = tv.tv_sec + wait_sec;
268
269       P(dev->mutex);
270       dev_blocked = dev->dev_blocked;
271       dev->dev_blocked = BST_WAITING_FOR_SYSOP; /* indicate waiting for mount */
272       jcr->JobStatus = jstat;
273       dir_send_job_status(jcr);
274
275       for ( ;!job_cancelled(jcr); ) {
276          Dmsg1(90, "I'm going to sleep on device %s\n", dev->dev_name);
277          stat = pthread_cond_timedwait(&dev->wait_next_vol, &dev->mutex, &timeout);
278          if (dev->dev_blocked == BST_WAITING_FOR_SYSOP) {
279             break;
280          }
281          /*         
282           * Someone other than us blocked the device (probably the
283           *  user via the Console program.   
284           * So, we continue waiting.
285           */
286          gettimeofday(&tv, &tz);
287          timeout.tv_nsec = 0;
288          timeout.tv_sec = tv.tv_sec + 10; /* wait 10 seconds */
289       }
290       dev->dev_blocked = dev_blocked;
291       V(dev->mutex);
292
293       if (stat == ETIMEDOUT) {
294          wait_sec *= 2;               /* double wait time */
295          if (wait_sec > max_wait) {   /* but not longer than maxtime */
296             wait_sec = max_wait;
297          }
298          num_wait++;
299          if (num_wait >= max_num_wait) {
300             Mmsg(&dev->errmsg, "Gave up waiting to mount Storage Device \"%s\" for Job %s\n", 
301                  jcr->dev_name, jcr->Job);
302             Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
303             Dmsg1(90, "Gave up waiting on device %s\n", dev->dev_name);
304             return 0;                 /* exceeded maximum waits */
305          }
306          continue;
307       }
308       if (stat == EINVAL) {
309          Mmsg2(&dev->errmsg, "pthread error in mount_next_volume stat=%d ERR=%s\n",      
310                stat, strerror(stat));
311          Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
312          return 0;
313       }
314       if (stat != 0) {
315          Jmsg(jcr, M_ERROR, 0, "pthread error in mount_next_volume stat=%d ERR=%s\n", stat,
316             strerror(stat));
317       }
318       Dmsg1(90, "Someone woke me for device %s\n", dev->dev_name);
319
320       /* Restart wait counters */
321       wait_sec = min_wait;
322       num_wait = 0;
323       /* If no VolumeName, and cannot get one, try again */
324       if (jcr->VolumeName[0] == 0 && 
325           !dir_find_next_appendable_volume(jcr)) {
326          Jmsg(jcr, M_MOUNT, 0, 
327 "You woke me up, but I cannot find any appendable\n\
328 volumes for Job=%s.\n", jcr->Job);
329          continue;
330       }       
331       break;
332    }
333    jcr->JobStatus = JS_Running;
334    dir_send_job_status(jcr);
335    Dmsg0(30, "leave dir_ask_sysop_to_mount_next_volume\n");
336    return 1;
337 }
338
339 /*
340  *   
341  *   Entered with device blocked and jcr->VolumeName is desired
342  *      volume.
343  *   Leaves with device blocked.
344  *
345  *   Returns: 1 on success (operator issues a mount command)
346  *            0 on failure
347  *              Note, must create dev->errmsg on error return.
348  *
349  */
350 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
351 {
352    int stat, jstat;
353    /* ******FIXME******* put these on config variable */
354    int min_wait = 60 * 60;
355    int max_wait = 24 * 60 * 60;
356    int max_num_wait = 9;              /* 5 waits =~ 1 day, then 1 day at a time */
357    int wait_sec;
358    int num_wait = 0;
359    int dev_blocked;
360    char *msg;
361    struct timeval tv;
362    struct timezone tz;
363    struct timespec timeout;
364
365    Dmsg0(30, "enter dir_ask_sysop_to_mount_next_volume\n");
366    if (!jcr->VolumeName[0]) {
367       Mmsg0(&dev->errmsg, "Cannot request another volume: no volume name given.\n");
368       return 0;
369    }
370    ASSERT(dev->dev_blocked);
371    wait_sec = min_wait;
372    for ( ;; ) {
373       if (job_cancelled(jcr)) {
374          Mmsg(&dev->errmsg, "Job %s cancelled while waiting for mount on Storage Device \"%s\".\n", 
375               jcr->Job, jcr->dev_name);
376          return 0;
377       }
378       msg = "Please mount";
379       Jmsg(jcr, M_MOUNT, 0, "%s Volume \"%s\" on Storage Device \"%s\" for Job %s\n",
380            msg, jcr->VolumeName, jcr->dev_name, jcr->Job);
381       Dmsg3(90, "Mount %s on %s for Job %s\n",
382             jcr->VolumeName, jcr->dev_name, jcr->Job);
383
384       /*
385        * Wait then send message again
386        */
387       gettimeofday(&tv, &tz);
388       timeout.tv_nsec = tv.tv_usec * 1000;
389       timeout.tv_sec = tv.tv_sec + wait_sec;
390
391       P(dev->mutex);
392       dev_blocked = dev->dev_blocked;
393       dev->dev_blocked = BST_WAITING_FOR_SYSOP; /* indicate waiting for mount */
394       jcr->JobStatus = jstat;
395       dir_send_job_status(jcr);
396
397       for ( ;!job_cancelled(jcr); ) {
398          Dmsg1(90, "I'm going to sleep on device %s\n", dev->dev_name);
399          stat = pthread_cond_timedwait(&dev->wait_next_vol, &dev->mutex, &timeout);
400          if (dev->dev_blocked == BST_WAITING_FOR_SYSOP) {
401             break;
402          }
403          /*         
404           * Someone other than us blocked the device (probably the
405           *  user via the Console program.   
406           * So, we continue waiting.
407           */
408          gettimeofday(&tv, &tz);
409          timeout.tv_nsec = 0;
410          timeout.tv_sec = tv.tv_sec + 10; /* wait 10 seconds */
411       }
412       dev->dev_blocked = dev_blocked;
413       V(dev->mutex);
414
415       if (stat == ETIMEDOUT) {
416          wait_sec *= 2;               /* double wait time */
417          if (wait_sec > max_wait) {   /* but not longer than maxtime */
418             wait_sec = max_wait;
419          }
420          num_wait++;
421          if (num_wait >= max_num_wait) {
422             Mmsg(&dev->errmsg, "Gave up waiting to mount Storage Device \"%s\" for Job %s\n", 
423                  jcr->dev_name, jcr->Job);
424             Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
425             Dmsg1(90, "Gave up waiting on device %s\n", dev->dev_name);
426             return 0;                 /* exceeded maximum waits */
427          }
428          continue;
429       }
430       if (stat == EINVAL) {
431          Mmsg2(&dev->errmsg, "pthread error in mount_volume stat=%d ERR=%s\n",      
432                stat, strerror(stat));
433          Jmsg(jcr, M_ERROR, 0, "%s", dev->errmsg);
434          return 0;
435       }
436       if (stat != 0) {
437          Jmsg(jcr, M_ERROR, 0, "pthread error in mount_next_volume stat=%d ERR=%s\n", stat,
438             strerror(stat));
439       }
440       Dmsg1(90, "Someone woke me for device %s\n", dev->dev_name);
441
442       /* Restart wait counters */
443       wait_sec = min_wait;
444       num_wait = 0;
445       break;
446    }
447    jcr->JobStatus = JS_Running;
448    dir_send_job_status(jcr);
449    Dmsg0(30, "leave dir_ask_sysop_to_mount_next_volume\n");
450    return 1;
451 }