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