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