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