]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/askdir.c
kes Implement code to pass the MediaId to the SD. The SD then uses
[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-2006 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    " VolFirstWritten=%s 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 MediaId=%s\n";
38 static char FileAttributes[] = "UpdCat Job=%s FileAttributes ";
39 static char Job_status[]     = "Status Job=%s JobStatus=%d\n";
40
41 /* Responses received from the Director */
42 static char OK_media[] = "1000 OK VolName=%127s VolJobs=%u VolFiles=%lu"
43    " VolBlocks=%lu VolBytes=%lld VolMounts=%lu VolErrors=%lu VolWrites=%lu"
44    " MaxVolBytes=%lld VolCapacityBytes=%lld VolStatus=%20s"
45    " Slot=%ld MaxVolJobs=%lu MaxVolFiles=%lu InChanger=%ld"
46    " VolReadTime=%lld VolWriteTime=%lld EndFile=%lu EndBlock=%lu"
47    " VolParts=%lu LabelType=%ld MediaId=%lld\n";
48
49
50 static char OK_create[] = "1000 OK CreateJobMedia\n";
51
52 #ifdef needed
53
54 static char Device_update[] = "DevUpd Job=%s device=%s "
55    "append=%d read=%d num_writers=%d "
56    "open=%d labeled=%d offline=%d "
57    "reserved=%d max_writers=%d "
58    "autoselect=%d autochanger=%d "
59    "changer_name=%s media_type=%s volume_name=%s\n";
60
61
62 /* Send update information about a device to Director */
63 bool dir_update_device(JCR *jcr, DEVICE *dev)
64 {
65    BSOCK *dir = jcr->dir_bsock;
66    POOL_MEM dev_name, VolumeName, MediaType, ChangerName;
67    DEVRES *device = dev->device;
68    bool ok;
69    
70    pm_strcpy(dev_name, device->hdr.name);
71    bash_spaces(dev_name);
72    if (dev->is_labeled()) {
73       pm_strcpy(VolumeName, dev->VolHdr.VolumeName);
74    } else {
75       pm_strcpy(VolumeName, "*");
76    }
77    bash_spaces(VolumeName);
78    pm_strcpy(MediaType, device->media_type);
79    bash_spaces(MediaType);
80    if (device->changer_res) {
81       pm_strcpy(ChangerName, device->changer_res->hdr.name);
82       bash_spaces(ChangerName);
83    } else {
84       pm_strcpy(ChangerName, "*");
85    }
86    ok =bnet_fsend(dir, Device_update, 
87       jcr->Job,
88       dev_name.c_str(),
89       dev->can_append()!=0,
90       dev->can_read()!=0, dev->num_writers, 
91       dev->is_open()!=0, dev->is_labeled()!=0,
92       dev->is_offline()!=0, dev->reserved_device, 
93       dev->is_tape()?100000:1,
94       dev->autoselect, 0, 
95       ChangerName.c_str(), MediaType.c_str(), VolumeName.c_str());
96    Dmsg1(100, ">dird: %s\n", dir->msg);
97    return ok;
98 }
99
100 bool dir_update_changer(JCR *jcr, AUTOCHANGER *changer)
101 {
102    BSOCK *dir = jcr->dir_bsock;
103    POOL_MEM dev_name, MediaType;
104    DEVRES *device;
105    bool ok;
106
107    pm_strcpy(dev_name, changer->hdr.name);
108    bash_spaces(dev_name);
109    device = (DEVRES *)changer->device->first();
110    pm_strcpy(MediaType, device->media_type);
111    bash_spaces(MediaType);
112    /* This is mostly to indicate that we are here */
113    ok = bnet_fsend(dir, Device_update,
114       jcr->Job,
115       dev_name.c_str(),         /* Changer name */
116       0, 0, 0,                  /* append, read, num_writers */
117       0, 0, 0,                  /* is_open, is_labeled, offline */
118       0, 0,                     /* reserved, max_writers */
119       0,                        /* Autoselect */
120       changer->device->size(),  /* Number of devices */
121       "0",                      /* PoolId */
122       "*",                      /* ChangerName */
123       MediaType.c_str(),        /* MediaType */
124       "*");                     /* VolName */
125    Dmsg1(100, ">dird: %s\n", dir->msg);
126    return ok;
127 }
128 #endif
129
130
131 /*
132  * Send current JobStatus to Director
133  */
134 bool dir_send_job_status(JCR *jcr)
135 {
136    return bnet_fsend(jcr->dir_bsock, Job_status, jcr->Job, jcr->JobStatus);
137 }
138
139 /*
140  * Common routine for:
141  *   dir_get_volume_info()
142  * and
143  *   dir_find_next_appendable_volume()
144  *
145  *  Returns: true  on success and vol info in dcr->VolCatInfo
146  *           false on failure
147  */
148 static bool do_get_volume_info(DCR *dcr)
149 {
150     JCR *jcr = dcr->jcr;
151     BSOCK *dir = jcr->dir_bsock;
152     VOLUME_CAT_INFO vol;
153     int n;
154     int32_t InChanger;
155
156     dcr->VolumeName[0] = 0;           /* No volume */
157     if (bnet_recv(dir) <= 0) {
158        Dmsg0(200, "getvolname error bnet_recv\n");
159        Mmsg(jcr->errmsg, _("Network error on bnet_recv in req_vol_info.\n"));
160        return false;
161     }
162     memset(&vol, 0, sizeof(vol));
163     Dmsg1(100, "<dird %s", dir->msg);
164     n = sscanf(dir->msg, OK_media, vol.VolCatName,
165                &vol.VolCatJobs, &vol.VolCatFiles,
166                &vol.VolCatBlocks, &vol.VolCatBytes,
167                &vol.VolCatMounts, &vol.VolCatErrors,
168                &vol.VolCatWrites, &vol.VolCatMaxBytes,
169                &vol.VolCatCapacityBytes, vol.VolCatStatus,
170                &vol.Slot, &vol.VolCatMaxJobs, &vol.VolCatMaxFiles,
171                &InChanger, &vol.VolReadTime, &vol.VolWriteTime,
172                &vol.EndFile, &vol.EndBlock, &vol.VolCatParts,
173                &vol.LabelType, &vol.VolMediaId);
174     if (n != 22) {
175        Dmsg2(100, "Bad response from Dir fields=%d: %s", n, dir->msg);
176        Mmsg(jcr->errmsg, _("Error getting Volume info: %s"), dir->msg);
177        return false;
178     }
179     vol.InChanger = InChanger;        /* bool in structure */
180     unbash_spaces(vol.VolCatName);
181     bstrncpy(dcr->VolumeName, vol.VolCatName, sizeof(dcr->VolumeName));
182     dcr->VolCatInfo = vol;            /* structure assignment */
183
184     Dmsg2(100, "do_reqest_vol_info return true slot=%d Volume=%s\n",
185           vol.Slot, vol.VolCatName);
186     return true;
187 }
188
189
190 /*
191  * Get Volume info for a specific volume from the Director's Database
192  *
193  * Returns: true  on success   (Director guarantees that Pool and MediaType
194  *                              are correct and VolStatus==Append or
195  *                              VolStatus==Recycle)
196  *          false on failure
197  *
198  *          Volume information returned in dcr->VolCatInfo
199  */
200 bool dir_get_volume_info(DCR *dcr, enum get_vol_info_rw writing)
201 {
202     JCR *jcr = dcr->jcr;
203     BSOCK *dir = jcr->dir_bsock;
204
205     bstrncpy(dcr->VolCatInfo.VolCatName, dcr->VolumeName, sizeof(dcr->VolCatInfo.VolCatName));
206     bash_spaces(dcr->VolCatInfo.VolCatName);
207     bnet_fsend(dir, Get_Vol_Info, jcr->Job, dcr->VolCatInfo.VolCatName,
208        writing==GET_VOL_INFO_FOR_WRITE?1:0);
209     Dmsg1(100, ">dird: %s", dir->msg);
210     bool ok = do_get_volume_info(dcr);
211     return ok;
212 }
213
214
215
216 /*
217  * Get info on the next appendable volume in the Director's database
218  * Returns: true  on success
219  *          false on failure
220  *
221  *          Volume information returned in dcr
222  *
223  */
224 bool dir_find_next_appendable_volume(DCR *dcr)
225 {
226     JCR *jcr = dcr->jcr;
227     BSOCK *dir = jcr->dir_bsock;
228     bool found = false;
229
230     Dmsg0(200, "dir_find_next_appendable_volume\n");
231     /*
232      * Try the twenty oldest or most available volumes.  Note,
233      *   the most available could already be mounted on another
234      *   drive, so we continue looking for a not in use Volume.
235      */
236     lock_reservations();
237     for (int vol_index=1;  vol_index < 20; vol_index++) {
238        bash_spaces(dcr->media_type);
239        bash_spaces(dcr->pool_name);
240        bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
241        unbash_spaces(dcr->media_type);
242        unbash_spaces(dcr->pool_name);
243        Dmsg1(100, ">dird: %s", dir->msg);
244        bool ok = do_get_volume_info(dcr);
245        if (ok) {
246           if (dcr->any_volume || !is_volume_in_use(dcr)) {
247              found = true;
248              break;
249           } else {
250              Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
251              continue;
252           }
253        } else {
254           Dmsg2(100, "No vol. index %d return false. dev=%s\n", vol_index,
255              dcr->dev->print_name());
256           found = false;
257           break;
258        }
259     }
260     if (found) {
261        Dmsg0(400, "dir_find_next_appendable_volume return true\n");
262        new_volume(dcr, dcr->VolumeName);   /* reserve volume */
263        unlock_reservations();
264        return true;
265     }
266     dcr->VolumeName[0] = 0;
267     unlock_reservations();
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    VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
284    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
285    int InChanger;
286    POOL_MEM VolumeName;
287
288    /* If system job, do not update catalog */
289    if (jcr->JobType == JT_SYSTEM) {
290       return true;
291    }
292
293    if (vol->VolCatName[0] == 0) {
294       Jmsg0(jcr, M_FATAL, 0, _("NULL Volume name. This shouldn't happen!!!\n"));
295       Pmsg0(000, _("NULL Volume name. This shouldn't happen!!!\n"));
296       return false;
297    }
298    if (dev->can_read()) {
299       Jmsg0(jcr, M_FATAL, 0, _("Attempt to update_volume_info in read mode!!!\n"));
300       Pmsg0(000, _("Attempt to update_volume_info in read mode!!!\n"));
301       return false;
302    }
303
304    Dmsg1(100, "Update cat VolFiles=%d\n", dev->file);
305    /* Just labeled or relabeled the tape */
306    if (label) {
307       bstrncpy(vol->VolCatStatus, "Append", sizeof(vol->VolCatStatus));
308    }
309    pm_strcpy(VolumeName, vol->VolCatName);
310    bash_spaces(VolumeName);
311    InChanger = vol->InChanger;
312    bnet_fsend(dir, Update_media, jcr->Job,
313       VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
314       vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
315       vol->VolCatMounts, vol->VolCatErrors,
316       vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
317       LastWritten, vol->VolCatStatus, vol->Slot, label,
318       InChanger,                      /* bool in structure */
319       edit_uint64(vol->VolReadTime, ed3),
320       edit_uint64(vol->VolWriteTime, ed4),
321       edit_uint64(vol->VolFirstWritten, ed5),
322       vol->VolCatParts);
323     Dmsg1(100, ">dird: %s", dir->msg);
324
325    /* Do not lock device here because it may be locked from label */
326    if (!do_get_volume_info(dcr)) {
327       Jmsg(jcr, M_FATAL, 0, "%s", jcr->errmsg);
328       Dmsg2(100, _("Didn't get vol info vol=%s: ERR=%s"), 
329          vol->VolCatName, jcr->errmsg);
330       return false;
331    }
332    Dmsg1(420, "get_volume_info(): %s", dir->msg);
333    /* Update dev Volume info in case something changed (e.g. expired) */
334    dev->VolCatInfo = dcr->VolCatInfo;
335    return true;
336 }
337
338 /*
339  * After writing a Volume, create the JobMedia record.
340  */
341 bool dir_create_jobmedia_record(DCR *dcr)
342 {
343    JCR *jcr = dcr->jcr;
344    BSOCK *dir = jcr->dir_bsock;
345    char ed1[50];
346
347    /* If system job, do not update catalog */
348    if (jcr->JobType == JT_SYSTEM) {
349       return true;
350    }
351
352    if (!dcr->WroteVol) {
353       return true;                    /* nothing written to tape */
354    }
355
356    dcr->WroteVol = false;
357    bnet_fsend(dir, Create_job_media, jcr->Job,
358       dcr->VolFirstIndex, dcr->VolLastIndex,
359       dcr->StartFile, dcr->EndFile,
360       dcr->StartBlock, dcr->EndBlock, 
361       dcr->Copy, dcr->Stripe, 
362       edit_uint64(dcr->dev->VolCatInfo.VolMediaId, ed1));
363     Dmsg1(100, ">dird: %s", dir->msg);
364    if (bnet_recv(dir) <= 0) {
365       Dmsg0(190, "create_jobmedia error bnet_recv\n");
366       Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
367            bnet_strerror(dir));
368       return false;
369    }
370    Dmsg1(100, "<dir: %s", dir->msg);
371    if (strcmp(dir->msg, OK_create) != 0) {
372       Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
373       Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
374       return false;
375    }
376    return true;
377 }
378
379
380 /*
381  * Update File Attribute data
382  */
383 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
384 {
385    JCR *jcr = dcr->jcr;
386    BSOCK *dir = jcr->dir_bsock;
387    ser_declare;
388
389 #ifdef NO_ATTRIBUTES_TEST
390    return true;
391 #endif
392
393    dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
394    dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
395                 sizeof(DEV_RECORD) + rec->data_len);
396    ser_begin(dir->msg + dir->msglen, 0);
397    ser_uint32(rec->VolSessionId);
398    ser_uint32(rec->VolSessionTime);
399    ser_int32(rec->FileIndex);
400    ser_int32(rec->Stream);
401    ser_uint32(rec->data_len);
402    ser_bytes(rec->data, rec->data_len);
403    dir->msglen = ser_length(dir->msg);
404    Dmsg1(1800, ">dird: %s\n", dir->msg);    /* Attributes */
405    return bnet_send(dir);
406 }
407
408
409 /*
410  *   Request the sysop to create an appendable volume
411  *
412  *   Entered with device blocked.
413  *   Leaves with device blocked.
414  *
415  *   Returns: true  on success (operator issues a mount command)
416  *            false on failure
417  *              Note, must create dev->errmsg on error return.
418  *
419  *    On success, dcr->VolumeName and dcr->VolCatInfo contain
420  *      information on suggested volume, but this may not be the
421  *      same as what is actually mounted.
422  *
423  *    When we return with success, the correct tape may or may not
424  *      actually be mounted. The calling routine must read it and
425  *      verify the label.
426  */
427 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
428 {
429    int stat = W_TIMEOUT;
430    DEVICE *dev = dcr->dev;
431    JCR *jcr = dcr->jcr;
432    bool got_vol = false;
433
434    Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
435    ASSERT(dev->dev_blocked);
436    for ( ;; ) {
437       if (job_canceled(jcr)) {
438          Mmsg(dev->errmsg,
439               _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
440               jcr->Job, dev->print_name());
441          Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
442          return false;
443       }
444       P(dev->mutex);
445       got_vol = dir_find_next_appendable_volume(dcr);   /* get suggested volume */
446       V(dev->mutex);
447       if (got_vol) {
448          return true;
449       } else {
450          if (stat == W_TIMEOUT || stat == W_MOUNT) {
451             Jmsg(jcr, M_MOUNT, 0, _(
452 "Job %s waiting. Cannot find any appendable volumes.\n"
453 "Please use the \"label\"  command to create a new Volume for:\n"
454 "    Storage:      %s\n"
455 "    Media type:   %s\n"
456 "    Pool:         %s\n"),
457                jcr->Job,
458                dev->print_name(),
459                dcr->media_type,
460                dcr->pool_name);
461          }
462       }
463
464       set_jcr_job_status(jcr, JS_WaitMedia);
465       dir_send_job_status(jcr);
466
467       stat = wait_for_sysop(dcr);
468       Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
469       if (dev->poll) {
470          Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
471          continue;
472       }
473
474       if (stat == W_TIMEOUT) {
475          if (!double_dev_wait_time(dev)) {
476             Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
477                dev->print_name(), jcr->Job);
478             Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
479             Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
480             return false;             /* exceeded maximum waits */
481          }
482          continue;
483       }
484       if (stat == W_ERROR) {
485          berrno be;
486          Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
487          Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
488          return false;
489       }
490       Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
491    }
492    set_jcr_job_status(jcr, JS_Running);
493    dir_send_job_status(jcr);
494    Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
495    return true;
496 }
497
498 /*
499  *   Request to mount specific Volume
500  *
501  *   Entered with device blocked and dcr->VolumeName is desired
502  *      volume.
503  *   Leaves with device blocked.
504  *
505  *   Returns: true  on success (operator issues a mount command)
506  *            false on failure
507  *                  Note, must create dev->errmsg on error return.
508  *
509  */
510 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
511 {
512    int stat = W_TIMEOUT;
513    DEVICE *dev = dcr->dev;
514    JCR *jcr = dcr->jcr;
515
516    Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
517    if (!dcr->VolumeName[0]) {
518       Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
519       return false;
520    }
521    ASSERT(dev->dev_blocked);
522    for ( ;; ) {
523       if (job_canceled(jcr)) {
524          Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
525               jcr->Job, dev->print_name());
526          return false;
527       }
528
529       if (dev->is_dvd()) {   
530          dev->unmount(0);
531       }
532       
533       /*
534        * If we are not polling, and the wait timeout or the
535        *   user explicitly did a mount, send him the message.
536        *   Otherwise skip it.
537        */
538       if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
539          Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
540               dcr->VolumeName, dev->print_name(), jcr->Job);
541          Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
542                dcr->VolumeName, dev->print_name(), jcr->Job);
543       }
544
545       set_jcr_job_status(jcr, JS_WaitMount);
546       dir_send_job_status(jcr);
547
548       stat = wait_for_sysop(dcr);          /* wait on device */
549       Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
550       if (dev->poll) {
551          Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
552          Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
553          return true;
554       }
555
556       if (stat == W_TIMEOUT) {
557          if (!double_dev_wait_time(dev)) {
558             Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
559                dev->print_name(), jcr->Job);
560             Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
561             Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
562             return false;             /* exceeded maximum waits */
563          }
564          continue;
565       }
566       if (stat == W_ERROR) {
567          berrno be;
568          Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
569          Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
570          return false;
571       }
572       Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
573       break;
574    }
575    set_jcr_job_status(jcr, JS_Running);
576    dir_send_job_status(jcr);
577    Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");
578    return true;
579 }