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