]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/catreq.c
kes Add back code to open tape device nonblocking, but if rewind fails
[bacula/bacula] / bacula / src / dird / catreq.c
1 /*
2  *
3  *   Bacula Director -- catreq.c -- handles the message channel
4  *    catalog request from the Storage daemon.
5  *
6  *     Kern Sibbald, March MMI
7  *
8  *    This routine runs as a thread and must be thread reentrant.
9  *
10  *  Basic tasks done here:
11  *      Handle Catalog services.
12  *
13  *   Version $Id$
14  */
15 /*
16    Copyright (C) 2001-2006 Kern Sibbald
17
18    This program is free software; you can redistribute it and/or
19    modify it under the terms of the GNU General Public License
20    version 2 as amended with additional clauses defined in the
21    file LICENSE in the main source directory.
22
23    This program is distributed in the hope that it will be useful,
24    but WITHOUT ANY WARRANTY; without even the implied warranty of
25    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
26    the file LICENSE for additional details.
27
28  */
29
30 #include "bacula.h"
31 #include "dird.h"
32 #include "findlib/find.h"
33
34 /*
35  * Handle catalog request
36  *  For now, we simply return next Volume to be used
37  */
38
39 /* Requests from the Storage daemon */
40 static char Find_media[] = "CatReq Job=%127s FindMedia=%d pool_name=%127s media_type=%127s\n";
41 static char Get_Vol_Info[] = "CatReq Job=%127s GetVolInfo VolName=%127s write=%d\n";
42
43 static char Update_media[] = "CatReq Job=%127s UpdateMedia VolName=%s"
44    " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%" lld " VolMounts=%u"
45    " VolErrors=%u VolWrites=%u MaxVolBytes=%" lld " EndTime=%d VolStatus=%10s"
46    " Slot=%d relabel=%d InChanger=%d VolReadTime=%" lld " VolWriteTime=%" lld
47    " VolFirstWritten=%" lld " VolParts=%u\n";
48
49 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
50    " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
51    " StartBlock=%u EndBlock=%u Copy=%d Strip=%d\n";
52
53
54 /* Responses  sent to Storage daemon */
55 static char OK_media[] = "1000 OK VolName=%s VolJobs=%u VolFiles=%u"
56    " VolBlocks=%u VolBytes=%s VolMounts=%u VolErrors=%u VolWrites=%u"
57    " MaxVolBytes=%s VolCapacityBytes=%s VolStatus=%s Slot=%d"
58    " MaxVolJobs=%u MaxVolFiles=%u InChanger=%d VolReadTime=%s"
59    " VolWriteTime=%s EndFile=%u EndBlock=%u VolParts=%u LabelType=%d\n";
60
61 static char OK_create[] = "1000 OK CreateJobMedia\n";
62
63
64 static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr)
65 {
66    int stat;
67    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
68
69    jcr->MediaId = mr->MediaId;
70    pm_strcpy(jcr->VolumeName, mr->VolumeName);
71    bash_spaces(mr->VolumeName);
72    stat = bnet_fsend(sd, OK_media, mr->VolumeName, mr->VolJobs,
73       mr->VolFiles, mr->VolBlocks, edit_uint64(mr->VolBytes, ed1),
74       mr->VolMounts, mr->VolErrors, mr->VolWrites,
75       edit_uint64(mr->MaxVolBytes, ed2),
76       edit_uint64(mr->VolCapacityBytes, ed3),
77       mr->VolStatus, mr->Slot, mr->MaxVolJobs, mr->MaxVolFiles,
78       mr->InChanger,
79       edit_uint64(mr->VolReadTime, ed4),
80       edit_uint64(mr->VolWriteTime, ed5),
81       mr->EndFile, mr->EndBlock,
82       mr->VolParts,
83       mr->LabelType);
84    unbash_spaces(mr->VolumeName);
85    Dmsg2(100, "Vol Info for %s: %s", jcr->Job, sd->msg);
86    return stat;
87 }
88
89 void catalog_request(JCR *jcr, BSOCK *bs)
90 {
91    MEDIA_DBR mr, sdmr;
92    JOBMEDIA_DBR jm;
93    char Job[MAX_NAME_LENGTH];
94    char pool_name[MAX_NAME_LENGTH];
95    int index, ok, label, writing;
96    POOLMEM *omsg;
97    POOL_DBR pr;
98    uint32_t Stripe;
99    utime_t VolFirstWritten;
100
101    memset(&mr, 0, sizeof(mr));
102    memset(&sdmr, 0, sizeof(sdmr));
103    memset(&jm, 0, sizeof(jm));
104
105    /*
106     * Request to find next appendable Volume for this Job
107     */
108    Dmsg1(100, "catreq %s", bs->msg);
109    if (!jcr->db) {
110       omsg = get_memory(bs->msglen+1);
111       pm_strcpy(omsg, bs->msg);
112       bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);    
113       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request; DB not open: %s"), omsg);
114       free_memory(omsg);
115       return;
116    }
117    /*
118     * Find next appendable medium for SD
119     */
120    if (sscanf(bs->msg, Find_media, &Job, &index, &pool_name, &mr.MediaType) == 4) {
121       memset(&pr, 0, sizeof(pr));
122       bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
123       unbash_spaces(pr.Name);
124       ok = db_get_pool_record(jcr, jcr->db, &pr);
125       if (ok) {
126          mr.PoolId = pr.PoolId;
127          mr.StorageId = jcr->wstore->StorageId;
128          ok = find_next_volume_for_append(jcr, &mr, index, true /*permit create new vol*/);
129          Dmsg3(100, "find_media idx=%d ok=%d vol=%s\n", index, ok, mr.VolumeName);
130       }
131       /*
132        * Send Find Media response to Storage daemon
133        */
134       if (ok) {
135          send_volume_info_to_storage_daemon(jcr, bs, &mr);
136       } else {
137          bnet_fsend(bs, _("1901 No Media.\n"));
138          Dmsg0(500, "1901 No Media.\n");
139       }
140
141    /*
142     * Request to find specific Volume information
143     */
144    } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing) == 3) {
145       Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName);
146       /*
147        * Find the Volume
148        */
149       unbash_spaces(mr.VolumeName);
150       if (db_get_media_record(jcr, jcr->db, &mr)) {
151          const char *reason = NULL;           /* detailed reason for rejection */
152          /*
153           * If we are reading, accept any volume (reason == NULL)
154           * If we are writing, check if the Volume is valid
155           *   for this job, and do a recycle if necessary
156           */
157          if (writing) {
158             /*
159              * SD wants to write this Volume, so make
160              *   sure it is suitable for this job, i.e.
161              *   Pool matches, and it is either Append or Recycle
162              *   and Media Type matches and Pool allows any volume.
163              */
164             if (mr.PoolId != jcr->jr.PoolId) {
165                reason = _("not in Pool");
166             } else if (strcmp(mr.MediaType, jcr->wstore->media_type) != 0) {
167                reason = _("not correct MediaType");
168             } else {
169                /*
170                 * Now try recycling if necessary
171                 *   reason set non-NULL if we cannot use it
172                 */
173                check_if_volume_valid_or_recyclable(jcr, &mr, &reason);
174             }
175          }
176          if (reason == NULL) {
177             /*
178              * Send Find Media response to Storage daemon
179              */
180             send_volume_info_to_storage_daemon(jcr, bs, &mr);
181          } else {
182             /* Not suitable volume */
183             bnet_fsend(bs, _("1998 Volume \"%s\" status is %s, %s.\n"), mr.VolumeName,
184                mr.VolStatus, reason);
185          }
186
187       } else {
188          bnet_fsend(bs, _("1997 Volume \"%s\" not in catalog.\n"), mr.VolumeName);
189          Dmsg1(100, "1997 Volume \"%s\" not in catalog.\n", mr.VolumeName);
190       }
191
192    /*
193     * Request to update Media record. Comes typically at the end
194     *  of a Storage daemon Job Session, when labeling/relabeling a
195     *  Volume, or when an EOF mark is written.
196     */
197    } else if (sscanf(bs->msg, Update_media, &Job, &sdmr.VolumeName,
198       &sdmr.VolJobs, &sdmr.VolFiles, &sdmr.VolBlocks, &sdmr.VolBytes,
199       &sdmr.VolMounts, &sdmr.VolErrors, &sdmr.VolWrites, &sdmr.MaxVolBytes,
200       &sdmr.LastWritten, &sdmr.VolStatus, &sdmr.Slot, &label, &sdmr.InChanger,
201       &sdmr.VolReadTime, &sdmr.VolWriteTime, &VolFirstWritten,
202       &sdmr.VolParts) == 19) {
203
204       db_lock(jcr->db);
205       Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName,
206          mr.VolStatus, sdmr.VolStatus);
207       bstrncpy(mr.VolumeName, sdmr.VolumeName, sizeof(mr.VolumeName)); /* copy Volume name */
208       unbash_spaces(mr.VolumeName);
209       if (!db_get_media_record(jcr, jcr->db, &mr)) {
210          Jmsg(jcr, M_ERROR, 0, _("Unable to get Media record for Volume %s: ERR=%s\n"),
211               mr.VolumeName, db_strerror(jcr->db));
212          bnet_fsend(bs, _("1991 Catalog Request for vol=%s failed: %s"),
213             mr.VolumeName, db_strerror(jcr->db));
214          db_unlock(jcr->db);
215          return;
216       }
217       /* Set first written time if this is first job */
218       if (mr.FirstWritten == 0) {
219          if (VolFirstWritten == 0) {
220             mr.FirstWritten = jcr->start_time;   /* use Job start time as first write */
221          } else {
222             mr.FirstWritten = VolFirstWritten;
223          }
224          mr.set_first_written = true;
225       }
226       /* If we just labeled the tape set time */
227       if (label || mr.LabelDate == 0) {
228          mr.LabelDate = jcr->start_time;
229          mr.set_label_date = true;
230          if (mr.InitialWrite == 0) {
231             mr.InitialWrite = jcr->start_time;
232          }
233          Dmsg2(400, "label=%d labeldate=%d\n", label, mr.LabelDate);
234       } else {
235          /*
236           * Insanity check for VolFiles get set to a smaller value
237           */
238          if (sdmr.VolFiles < mr.VolFiles) {
239             Jmsg(jcr, M_FATAL, 0, _("Volume Files at %u being set to %u"
240                  " for Volume \"%s\". This is incorrect.\n"),
241                mr.VolFiles, sdmr.VolFiles, mr.VolumeName);
242             bnet_fsend(bs, _("1992 Update Media error. VolFiles=%u, CatFiles=%u\n"),
243                sdmr.VolFiles, mr.VolFiles);
244             db_unlock(jcr->db);
245             return;
246          }
247       }
248       Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs);
249       /* Copy updated values to original media record */
250       mr.VolJobs      = sdmr.VolJobs;
251       mr.VolFiles     = sdmr.VolFiles;
252       mr.VolBlocks    = sdmr.VolBlocks;
253       mr.VolBytes     = sdmr.VolBytes;
254       mr.VolMounts    = sdmr.VolMounts;
255       mr.VolErrors    = sdmr.VolErrors;
256       mr.VolWrites    = sdmr.VolWrites;
257       mr.LastWritten  = sdmr.LastWritten;
258       mr.Slot         = sdmr.Slot;
259       mr.InChanger    = sdmr.InChanger;
260       mr.VolReadTime  = sdmr.VolReadTime;
261       mr.VolWriteTime = sdmr.VolWriteTime;
262       mr.VolParts     = sdmr.VolParts;
263       bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
264       if (jcr->wstore->StorageId) {
265          mr.StorageId = jcr->wstore->StorageId;
266       }
267
268       Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
269       /*
270        * Update the database, then before sending the response to the
271        *  SD, check if the Volume has expired.
272        */
273       if (!db_update_media_record(jcr, jcr->db, &mr)) {
274          Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"),
275             db_strerror(jcr->db));
276          bnet_fsend(bs, _("1993 Update Media error\n"));
277          Dmsg0(400, "send error\n");
278       } else {
279          (void)has_volume_expired(jcr, &mr);
280          send_volume_info_to_storage_daemon(jcr, bs, &mr);
281       }
282       db_unlock(jcr->db);
283
284    /*
285     * Request to create a JobMedia record
286     */
287    } else if (sscanf(bs->msg, Create_job_media, &Job,
288       &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile,
289       &jm.StartBlock, &jm.EndBlock, &jm.Copy, &Stripe) == 9) {
290
291       if (jcr->mig_jcr) {
292          jm.JobId = jcr->mig_jcr->JobId;
293          jm.MediaId = jcr->MediaId;
294       } else {
295          jm.JobId = jcr->JobId;
296          jm.MediaId = jcr->MediaId;
297       }
298       Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n",
299          jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex);
300       if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) {
301          Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"),
302             db_strerror(jcr->db));
303          bnet_fsend(bs, _("1991 Update JobMedia error\n"));
304       } else {
305          Dmsg0(400, "JobMedia record created\n");
306          bnet_fsend(bs, OK_create);
307       }
308
309    } else {
310       omsg = get_memory(bs->msglen+1);
311       pm_strcpy(omsg, bs->msg);
312       bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);
313       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg);
314       free_memory(omsg);
315    }
316    Dmsg1(400, ">CatReq response: %s", bs->msg);
317    Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
318    return;
319 }
320
321 /*
322  * Update File Attributes in the catalog with data
323  *  sent by the Storage daemon.  Note, we receive the whole
324  *  attribute record, but we select out only the stat packet,
325  *  VolSessionId, VolSessionTime, FileIndex, and file name
326  *  to store in the catalog.
327  */
328 void catalog_update(JCR *jcr, BSOCK *bs)
329 {
330    unser_declare;
331    uint32_t VolSessionId, VolSessionTime;
332    int32_t Stream;
333    uint32_t FileIndex;
334    uint32_t data_len;
335    char *p;
336    int len;
337    char *fname, *attr;
338    ATTR_DBR *ar = NULL;
339    POOLMEM *omsg;
340
341    if (!jcr->pool->catalog_files) {
342       return;                         /* user disabled cataloging */
343    }
344    if (!jcr->db) {
345       omsg = get_memory(bs->msglen+1);
346       pm_strcpy(omsg, bs->msg);
347       bnet_fsend(bs, _("1991 Invalid Catalog Update: %s"), omsg);    
348       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog Update; DB not open: %s"), omsg);
349       free_memory(omsg);
350       return;
351    }
352
353    /* Start transaction allocates jcr->attr and jcr->ar if needed */
354    db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
355    ar = jcr->ar;      
356
357    /* Start by scanning directly in the message buffer to get Stream   
358     *  there may be a cached attr so we cannot yet write into
359     *  jcr->attr or jcr->ar  
360     */
361    p = bs->msg;
362    skip_nonspaces(&p);                /* UpdCat */
363    skip_spaces(&p);
364    skip_nonspaces(&p);                /* Job=nnn */
365    skip_spaces(&p);
366    skip_nonspaces(&p);                /* FileAttributes */
367    p += 1;
368    unser_begin(p, 0);
369    unser_uint32(VolSessionId);
370    unser_uint32(VolSessionTime);
371    unser_int32(FileIndex);
372    unser_int32(Stream);
373    unser_uint32(data_len);
374    p += unser_length(p);
375
376    Dmsg1(400, "UpdCat msg=%s\n", bs->msg);
377    Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d data_len=%d\n",
378       VolSessionId, VolSessionTime, FileIndex, Stream, data_len);
379
380    if (Stream == STREAM_UNIX_ATTRIBUTES || Stream == STREAM_UNIX_ATTRIBUTES_EX) {
381       if (jcr->cached_attribute) {
382          Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
383          if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
384             Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
385          }
386       }
387       /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
388       jcr->attr = check_pool_memory_size(jcr->attr, bs->msglen);
389       memcpy(jcr->attr, bs->msg, bs->msglen);
390       p = jcr->attr - bs->msg + p;    /* point p into jcr->attr */
391       skip_nonspaces(&p);             /* skip FileIndex */
392       skip_spaces(&p);
393       skip_nonspaces(&p);             /* skip FileType */
394       skip_spaces(&p);
395       fname = p;
396       len = strlen(fname);        /* length before attributes */
397       attr = &fname[len+1];
398
399       Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
400       Dmsg1(400, "dird<stored: attr=%s\n", attr);
401       ar->attr = attr;
402       ar->fname = fname;
403       ar->FileIndex = FileIndex;
404       ar->Stream = Stream;
405       ar->link = NULL;
406       if (jcr->mig_jcr) {
407          ar->JobId = jcr->mig_jcr->JobId;
408       } else {
409          ar->JobId = jcr->JobId;
410       }
411       ar->Digest = NULL;
412       ar->DigestType = CRYPTO_DIGEST_NONE;
413       jcr->cached_attribute = true;
414
415       Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
416       Dmsg1(400, "dird<filed: attr=%s\n", attr);
417
418    } else if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) {
419       fname = p;
420       if (ar->FileIndex != FileIndex) {
421          Jmsg(jcr, M_WARNING, 0, _("Got %s but not same File as attributes\n"), stream_to_ascii(Stream));
422       } else {
423          /* Update digest in catalog */
424          char digestbuf[BASE64_SIZE(CRYPTO_DIGEST_MAX_SIZE)];
425          int len = 0;
426          int type = CRYPTO_DIGEST_NONE;
427
428          switch(Stream) {
429          case STREAM_MD5_DIGEST:
430             len = CRYPTO_DIGEST_MD5_SIZE;
431             type = CRYPTO_DIGEST_MD5;
432             break;
433          case STREAM_SHA1_DIGEST:
434             len = CRYPTO_DIGEST_SHA1_SIZE;
435             type = CRYPTO_DIGEST_SHA1;
436             break;
437          case STREAM_SHA256_DIGEST:
438             len = CRYPTO_DIGEST_SHA256_SIZE;
439             type = CRYPTO_DIGEST_SHA256;
440             break;
441          case STREAM_SHA512_DIGEST:
442             len = CRYPTO_DIGEST_SHA512_SIZE;
443             type = CRYPTO_DIGEST_SHA512;
444             break;
445          default:
446             /* Never reached ... */
447             Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. Unsupported digest stream type: %d"),
448                  Stream);
449          }
450
451          bin_to_base64(digestbuf, sizeof(digestbuf), fname, len, true);
452          Dmsg3(400, "DigestLen=%d Digest=%s type=%d\n", strlen(digestbuf), digestbuf, Stream);
453          if (jcr->cached_attribute) {
454             ar->Digest = digestbuf;
455             ar->DigestType = type;
456             Dmsg2(400, "Cached attr with digest. Stream=%d fname=%s\n", ar->Stream, ar->fname);
457             if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
458                Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
459             }
460             jcr->cached_attribute = false; 
461          } else {
462             if (!db_add_digest_to_file_record(jcr, jcr->db, ar->FileId, digestbuf, type)) {
463                Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. %s"),
464                   db_strerror(jcr->db));
465             }
466          }
467       }
468    }
469 }