]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/stored/askdir.c
- Add disk-changer to scripts directory + configure/Makefile
[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
218 /*
219  * Get info on the next appendable volume in the Director's database
220  * Returns: true  on success
221  *          false on failure
222  *
223  *          Volume information returned in dcr
224  *
225  */
226 bool dir_find_next_appendable_volume(DCR *dcr)
227 {
228     JCR *jcr = dcr->jcr;
229     BSOCK *dir = jcr->dir_bsock;
230     bool found = false;
231     /* This mutex should keep different devices from getting the
232      * same Volume.  
233      */
234     static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
235
236     Dmsg0(200, "dir_find_next_appendable_volume\n");
237     /*
238      * Try the twenty oldest or most available volumes.  Note,
239      *   the most available could already be mounted on another
240      *   drive, so we continue looking for a not in use Volume.
241      */
242     P(mutex);
243     for (int vol_index=1;  vol_index < 20; vol_index++) {
244        bash_spaces(dcr->media_type);
245        bash_spaces(dcr->pool_name);
246        bnet_fsend(dir, Find_media, jcr->Job, vol_index, dcr->pool_name, dcr->media_type);
247        unbash_spaces(dcr->media_type);
248        unbash_spaces(dcr->pool_name);
249        Dmsg1(100, ">dird: %s", dir->msg);
250        bool ok = do_get_volume_info(dcr);
251        if (ok) {
252           if (dcr->any_volume || !is_volume_in_use(dcr)) {
253              found = true;
254              break;
255           } else {
256              Dmsg1(100, "Volume %s is in use.\n", dcr->VolumeName);
257              continue;
258           }
259        } else {
260           Dmsg0(200, "No volume info, return false\n");
261           found = false;
262           break;
263        }
264     }
265     if (found) {
266        Dmsg0(400, "dir_find_next_appendable_volume return true\n");
267        new_volume(dcr, dcr->VolumeName);   /* reserve volume */
268        V(mutex);
269        return true;
270     }
271     dcr->VolumeName[0] = 0;
272     V(mutex);
273     return false;
274 }
275
276
277 /*
278  * After writing a Volume, send the updated statistics
279  * back to the director. The information comes from the
280  * dev record.
281  */
282 bool dir_update_volume_info(DCR *dcr, bool label)
283 {
284    JCR *jcr = dcr->jcr;
285    BSOCK *dir = jcr->dir_bsock;
286    DEVICE *dev = dcr->dev;
287    time_t LastWritten = time(NULL);
288    char ed1[50], ed2[50], ed3[50], ed4[50];
289    VOLUME_CAT_INFO *vol = &dev->VolCatInfo;
290    int InChanger;
291    POOL_MEM VolumeName;
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       vol->VolCatBytes = 1;           /* indicates tape labeled */
309    }
310    pm_strcpy(VolumeName, vol->VolCatName);
311    bash_spaces(VolumeName);
312    InChanger = vol->InChanger;
313    bnet_fsend(dir, Update_media, jcr->Job,
314       VolumeName.c_str(), vol->VolCatJobs, vol->VolCatFiles,
315       vol->VolCatBlocks, edit_uint64(vol->VolCatBytes, ed1),
316       vol->VolCatMounts, vol->VolCatErrors,
317       vol->VolCatWrites, edit_uint64(vol->VolCatMaxBytes, ed2),
318       LastWritten, vol->VolCatStatus, vol->Slot, label,
319       InChanger,                      /* bool in structure */
320       edit_uint64(vol->VolReadTime, ed3),
321       edit_uint64(vol->VolWriteTime, ed4),
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       Pmsg2(000, _("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    memcpy(&dev->VolCatInfo, &dcr->VolCatInfo, sizeof(dev->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
346    if (!dcr->WroteVol) {
347       return true;                    /* nothing written to tape */
348    }
349
350    dcr->WroteVol = false;
351    bnet_fsend(dir, Create_job_media, jcr->Job,
352       dcr->VolFirstIndex, dcr->VolLastIndex,
353       dcr->StartFile, dcr->EndFile,
354       dcr->StartBlock, dcr->EndBlock, 
355       dcr->Copy, dcr->Stripe);
356     Dmsg1(100, ">dird: %s", dir->msg);
357    if (bnet_recv(dir) <= 0) {
358       Dmsg0(190, "create_jobmedia error bnet_recv\n");
359       Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: ERR=%s\n"),
360            bnet_strerror(dir));
361       return false;
362    }
363    Dmsg1(100, "<dir: %s", dir->msg);
364    if (strcmp(dir->msg, OK_create) != 0) {
365       Dmsg1(130, "Bad response from Dir: %s\n", dir->msg);
366       Jmsg(jcr, M_FATAL, 0, _("Error creating JobMedia record: %s\n"), dir->msg);
367       return false;
368    }
369    return true;
370 }
371
372
373 /*
374  * Update File Attribute data
375  */
376 bool dir_update_file_attributes(DCR *dcr, DEV_RECORD *rec)
377 {
378    JCR *jcr = dcr->jcr;
379    BSOCK *dir = jcr->dir_bsock;
380    ser_declare;
381
382 #ifdef NO_ATTRIBUTES_TEST
383    return true;
384 #endif
385
386    dir->msglen = sprintf(dir->msg, FileAttributes, jcr->Job);
387    dir->msg = check_pool_memory_size(dir->msg, dir->msglen +
388                 sizeof(DEV_RECORD) + rec->data_len);
389    ser_begin(dir->msg + dir->msglen, 0);
390    ser_uint32(rec->VolSessionId);
391    ser_uint32(rec->VolSessionTime);
392    ser_int32(rec->FileIndex);
393    ser_int32(rec->Stream);
394    ser_uint32(rec->data_len);
395    ser_bytes(rec->data, rec->data_len);
396    dir->msglen = ser_length(dir->msg);
397    Dmsg1(1800, ">dird: %s\n", dir->msg);    /* Attributes */
398    return bnet_send(dir);
399 }
400
401
402 /*
403  *   Request the sysop to create an appendable volume
404  *
405  *   Entered with device blocked.
406  *   Leaves with device blocked.
407  *
408  *   Returns: true  on success (operator issues a mount command)
409  *            false on failure
410  *              Note, must create dev->errmsg on error return.
411  *
412  *    On success, dcr->VolumeName and dcr->VolCatInfo contain
413  *      information on suggested volume, but this may not be the
414  *      same as what is actually mounted.
415  *
416  *    When we return with success, the correct tape may or may not
417  *      actually be mounted. The calling routine must read it and
418  *      verify the label.
419  */
420 bool dir_ask_sysop_to_create_appendable_volume(DCR *dcr)
421 {
422    int stat = W_TIMEOUT;
423    DEVICE *dev = dcr->dev;
424    JCR *jcr = dcr->jcr;
425    bool got_vol = false;
426
427    Dmsg0(400, "enter dir_ask_sysop_to_create_appendable_volume\n");
428    ASSERT(dev->dev_blocked);
429    for ( ;; ) {
430       if (job_canceled(jcr)) {
431          Mmsg(dev->errmsg,
432               _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"),
433               jcr->Job, dev->print_name());
434          Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
435          return false;
436       }
437       P(dev->mutex);
438       got_vol = dir_find_next_appendable_volume(dcr);   /* get suggested volume */
439       V(dev->mutex);
440       if (got_vol) {
441          return true;
442       } else {
443          if (stat == W_TIMEOUT || stat == W_MOUNT) {
444             Jmsg(jcr, M_MOUNT, 0, _(
445 "Job %s waiting. Cannot find any appendable volumes.\n"
446 "Please use the \"label\"  command to create a new Volume for:\n"
447 "    Storage:      %s\n"
448 "    Media type:   %s\n"
449 "    Pool:         %s\n"),
450                jcr->Job,
451                dev->print_name(),
452                dcr->media_type,
453                dcr->pool_name);
454          }
455       }
456
457       jcr->JobStatus = JS_WaitMedia;
458       dir_send_job_status(jcr);
459
460       stat = wait_for_sysop(dcr);
461       Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
462       if (dev->poll) {
463          Dmsg1(100, "Poll timeout in create append vol on device %s\n", dev->print_name());
464          continue;
465       }
466
467       if (stat == W_TIMEOUT) {
468          if (!double_dev_wait_time(dev)) {
469             Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
470                dev->print_name(), jcr->Job);
471             Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
472             Dmsg1(100, "Gave up waiting on device %s\n", dev->print_name());
473             return false;             /* exceeded maximum waits */
474          }
475          continue;
476       }
477       if (stat == W_ERROR) {
478          berrno be;
479          Mmsg0(dev->errmsg, _("pthread error in mount_next_volume.\n"));
480          Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
481          return false;
482       }
483       Dmsg1(100, "Someone woke me for device %s\n", dev->print_name());
484    }
485    set_jcr_job_status(jcr, JS_Running);
486    dir_send_job_status(jcr);
487    Dmsg0(100, "leave dir_ask_sysop_to_mount_create_appendable_volume\n");
488    return true;
489 }
490
491 /*
492  *   Request to mount specific Volume
493  *
494  *   Entered with device blocked and dcr->VolumeName is desired
495  *      volume.
496  *   Leaves with device blocked.
497  *
498  *   Returns: true  on success (operator issues a mount command)
499  *            false on failure
500  *                  Note, must create dev->errmsg on error return.
501  *
502  */
503 bool dir_ask_sysop_to_mount_volume(DCR *dcr)
504 {
505    int stat = W_TIMEOUT;
506    DEVICE *dev = dcr->dev;
507    JCR *jcr = dcr->jcr;
508
509    Dmsg0(400, "enter dir_ask_sysop_to_mount_volume\n");
510    if (!dcr->VolumeName[0]) {
511       Mmsg0(dev->errmsg, _("Cannot request another volume: no volume name given.\n"));
512       return false;
513    }
514    ASSERT(dev->dev_blocked);
515    for ( ;; ) {
516       if (job_canceled(jcr)) {
517          Mmsg(dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device %s.\n"),
518               jcr->Job, dev->print_name());
519          return false;
520       }
521
522       if (dev->is_dvd()) {   
523          unmount_dvd(dev, 0);
524       }
525       
526       /*
527        * If we are not polling, and the wait timeout or the
528        *   user explicitly did a mount, send him the message.
529        *   Otherwise skip it.
530        */
531       if (!dev->poll && (stat == W_TIMEOUT || stat == W_MOUNT)) {
532          Jmsg(jcr, M_MOUNT, 0, _("Please mount Volume \"%s\" on Storage Device %s for Job %s\n"),
533               dcr->VolumeName, dev->print_name(), jcr->Job);
534          Dmsg3(400, "Mount \"%s\" on device \"%s\" for Job %s\n",
535                dcr->VolumeName, dev->print_name(), jcr->Job);
536       }
537
538       jcr->JobStatus = JS_WaitMount;
539       dir_send_job_status(jcr);
540
541       stat = wait_for_sysop(dcr);          /* wait on device */
542       Dmsg1(100, "Back from wait_for_sysop stat=%d\n", stat);
543       if (dev->poll) {
544          Dmsg1(400, "Poll timeout in mount vol on device %s\n", dev->print_name());
545          Dmsg1(400, "Blocked=%s\n", dev->print_blocked());
546          return true;
547       }
548
549       if (stat == W_TIMEOUT) {
550          if (!double_dev_wait_time(dev)) {
551             Mmsg(dev->errmsg, _("Max time exceeded waiting to mount Storage Device %s for Job %s\n"),
552                dev->print_name(), jcr->Job);
553             Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
554             Dmsg1(400, "Gave up waiting on device %s\n", dev->print_name());
555             return false;             /* exceeded maximum waits */
556          }
557          continue;
558       }
559       if (stat == W_ERROR) {
560          berrno be;
561          Mmsg(dev->errmsg, _("pthread error in mount_volume\n"));
562          Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
563          return false;
564       }
565       Dmsg1(400, "Someone woke me for device %s\n", dev->print_name());
566       break;
567    }
568    set_jcr_job_status(jcr, JS_Running);
569    dir_send_job_status(jcr);
570    Dmsg0(400, "leave dir_ask_sysop_to_mount_volume\n");
571    return true;
572 }