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