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