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