]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/catreq.c
- Add code to catalog_update to detect NULL db pointer.
[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
33 /*
34  * Handle catalog request
35  *  For now, we simply return next Volume to be used
36  */
37
38 /* Requests from the Storage daemon */
39 static char Find_media[] = "CatReq Job=%127s FindMedia=%d pool_name=%127s media_type=%127s\n";
40 static char Get_Vol_Info[] = "CatReq Job=%127s GetVolInfo VolName=%127s write=%d\n";
41
42 static char Update_media[] = "CatReq Job=%127s UpdateMedia VolName=%s"
43    " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%" lld " VolMounts=%u"
44    " VolErrors=%u VolWrites=%u MaxVolBytes=%" lld " EndTime=%d VolStatus=%10s"
45    " Slot=%d relabel=%d InChanger=%d VolReadTime=%" lld " VolWriteTime=%" lld
46    " VolParts=%u\n";
47
48 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
49    " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
50    " StartBlock=%u EndBlock=%u Copy=%d Strip=%d\n";
51
52
53 /* Responses  sent to Storage daemon */
54 static char OK_media[] = "1000 OK VolName=%s VolJobs=%u VolFiles=%u"
55    " VolBlocks=%u VolBytes=%s VolMounts=%u VolErrors=%u VolWrites=%u"
56    " MaxVolBytes=%s VolCapacityBytes=%s VolStatus=%s Slot=%d"
57    " MaxVolJobs=%u MaxVolFiles=%u InChanger=%d VolReadTime=%s"
58    " VolWriteTime=%s EndFile=%u EndBlock=%u VolParts=%u LabelType=%d\n";
59
60 static char OK_create[] = "1000 OK CreateJobMedia\n";
61
62
63 static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr)
64 {
65    int stat;
66    char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
67
68    jcr->MediaId = mr->MediaId;
69    pm_strcpy(jcr->VolumeName, mr->VolumeName);
70    bash_spaces(mr->VolumeName);
71    stat = bnet_fsend(sd, OK_media, mr->VolumeName, mr->VolJobs,
72       mr->VolFiles, mr->VolBlocks, edit_uint64(mr->VolBytes, ed1),
73       mr->VolMounts, mr->VolErrors, mr->VolWrites,
74       edit_uint64(mr->MaxVolBytes, ed2),
75       edit_uint64(mr->VolCapacityBytes, ed3),
76       mr->VolStatus, mr->Slot, mr->MaxVolJobs, mr->MaxVolFiles,
77       mr->InChanger,
78       edit_uint64(mr->VolReadTime, ed4),
79       edit_uint64(mr->VolWriteTime, ed5),
80       mr->EndFile, mr->EndBlock,
81       mr->VolParts,
82       mr->LabelType);
83    unbash_spaces(mr->VolumeName);
84    Dmsg2(100, "Vol Info for %s: %s", jcr->Job, sd->msg);
85    return stat;
86 }
87
88 void catalog_request(JCR *jcr, BSOCK *bs)
89 {
90    MEDIA_DBR mr, sdmr;
91    JOBMEDIA_DBR jm;
92    char Job[MAX_NAME_LENGTH];
93    char pool_name[MAX_NAME_LENGTH];
94    int index, ok, label, writing;
95    POOLMEM *omsg;
96    POOL_DBR pr;
97
98    memset(&mr, 0, sizeof(mr));
99    memset(&sdmr, 0, sizeof(sdmr));
100    memset(&jm, 0, sizeof(jm));
101
102    /*
103     * Request to find next appendable Volume for this Job
104     */
105    Dmsg1(100, "catreq %s", bs->msg);
106    if (!jcr->db) {
107       omsg = get_memory(bs->msglen+1);
108       pm_strcpy(omsg, bs->msg);
109       bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);    
110       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request; DB not open: %s"), omsg);
111       free_memory(omsg);
112       return;
113    }
114    /*
115     * Find next appendable medium for SD
116     */
117    if (sscanf(bs->msg, Find_media, &Job, &index, &pool_name, &mr.MediaType) == 4) {
118       memset(&pr, 0, sizeof(pr));
119       bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
120       ok = db_get_pool_record(jcr, jcr->db, &pr);
121       if (ok) {
122          mr.PoolId = pr.PoolId;
123          ok = find_next_volume_for_append(jcr, &mr, true /*permit create new vol*/);
124       }
125       /*
126        * Send Find Media response to Storage daemon
127        */
128       if (ok) {
129          send_volume_info_to_storage_daemon(jcr, bs, &mr);
130       } else {
131          bnet_fsend(bs, _("1901 No Media.\n"));
132          Dmsg0(500, "1901 No Media.\n");
133       }
134
135    /*
136     * Request to find specific Volume information
137     */
138    } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing) == 3) {
139       Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName);
140       /*
141        * Find the Volume
142        */
143       unbash_spaces(mr.VolumeName);
144       if (db_get_media_record(jcr, jcr->db, &mr)) {
145          const char *reason = NULL;           /* detailed reason for rejection */
146          /*
147           * If we are reading, accept any volume (reason == NULL)
148           * If we are writing, check if the Volume is valid
149           *   for this job, and do a recycle if necessary
150           */
151          if (writing) {
152             /*
153              * SD wants to write this Volume, so make
154              *   sure it is suitable for this job, i.e.
155              *   Pool matches, and it is either Append or Recycle
156              *   and Media Type matches and Pool allows any volume.
157              */
158             if (mr.PoolId != jcr->PoolId) {
159                reason = _("not in Pool");
160             } else if (strcmp(mr.MediaType, jcr->store->media_type) != 0) {
161                reason = _("not correct MediaType");
162             } else {
163               /*
164                * ****FIXME***
165                *   This test (accept_any_volume) is turned off
166                *   because it doesn't properly check if the volume
167                *   really is out of sequence!
168                *
169                * } else if (!jcr->pool->accept_any_volume) {
170                *    reason = "Volume not in sequence";
171                */
172
173                /*
174                 * Now try recycling if necessary
175                 *   reason set non-NULL if we cannot use it
176                 */
177                check_if_volume_valid_or_recyclable(jcr, &mr, &reason);
178             }
179          }
180          if (reason == NULL) {
181             /*
182              * Send Find Media response to Storage daemon
183              */
184             send_volume_info_to_storage_daemon(jcr, bs, &mr);
185          } else {
186             /* Not suitable volume */
187             bnet_fsend(bs, _("1998 Volume \"%s\" status is %s, %s.\n"), mr.VolumeName,
188                mr.VolStatus, reason);
189          }
190
191       } else {
192          bnet_fsend(bs, _("1997 Volume \"%s\" not in catalog.\n"), mr.VolumeName);
193          Dmsg1(100, "1997 Volume \"%s\" not in catalog.\n", mr.VolumeName);
194       }
195
196    /*
197     * Request to update Media record. Comes typically at the end
198     *  of a Storage daemon Job Session, when labeling/relabeling a
199     *  Volume, or when an EOF mark is written.
200     */
201    } else if (sscanf(bs->msg, Update_media, &Job, &sdmr.VolumeName,
202       &sdmr.VolJobs, &sdmr.VolFiles, &sdmr.VolBlocks, &sdmr.VolBytes,
203       &sdmr.VolMounts, &sdmr.VolErrors, &sdmr.VolWrites, &sdmr.MaxVolBytes,
204       &sdmr.LastWritten, &sdmr.VolStatus, &sdmr.Slot, &label, &sdmr.InChanger,
205       &sdmr.VolReadTime, &sdmr.VolWriteTime, &sdmr.VolParts) == 18) {
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          mr.FirstWritten = jcr->start_time;   /* use Job start time as first write */
223          mr.set_first_written = true;
224       }
225       /* If we just labeled the tape set time */
226       if (label || mr.LabelDate == 0) {
227          mr.LabelDate = jcr->start_time;
228          mr.set_label_date = true;
229          Dmsg2(400, "label=%d labeldate=%d\n", label, mr.LabelDate);
230       } else {
231          /*
232           * Insanity check for VolFiles get set to a smaller value
233           */
234          if (sdmr.VolFiles < mr.VolFiles) {
235             Jmsg(jcr, M_FATAL, 0, _("Volume Files at %u being set to %u"
236                  " for Volume \"%s\". This is incorrect.\n"),
237                mr.VolFiles, sdmr.VolFiles, mr.VolumeName);
238             bnet_fsend(bs, _("1992 Update Media error\n"));
239             db_unlock(jcr->db);
240             return;
241          }
242       }
243       Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs);
244       /* Copy updated values to original media record */
245       mr.VolJobs      = sdmr.VolJobs;
246       mr.VolFiles     = sdmr.VolFiles;
247       mr.VolBlocks    = sdmr.VolBlocks;
248       mr.VolBytes     = sdmr.VolBytes;
249       mr.VolMounts    = sdmr.VolMounts;
250       mr.VolErrors    = sdmr.VolErrors;
251       mr.VolWrites    = sdmr.VolWrites;
252       mr.LastWritten  = sdmr.LastWritten;
253       mr.Slot         = sdmr.Slot;
254       mr.InChanger    = sdmr.InChanger;
255       mr.VolReadTime  = sdmr.VolReadTime;
256       mr.VolWriteTime = sdmr.VolWriteTime;
257       mr.VolParts     = sdmr.VolParts;
258       bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
259
260       Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
261       /*
262        * Check if it has expired, and if not update the DB. Note, if
263        *   Volume has expired, has_volume_expired() will update the DB.
264        */
265       if (has_volume_expired(jcr, &mr) || db_update_media_record(jcr, jcr->db, &mr)) {
266          send_volume_info_to_storage_daemon(jcr, bs, &mr);
267       } else {
268          Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"),
269             db_strerror(jcr->db));
270          bnet_fsend(bs, _("1992 Update Media error\n"));
271          Dmsg0(400, "send error\n");
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       jm.JobId = jcr->JobId;
283       jm.MediaId = jcr->MediaId;
284       Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n",
285          jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex);
286       if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) {
287          Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"),
288             db_strerror(jcr->db));
289          bnet_fsend(bs, _("1991 Update JobMedia error\n"));
290       } else {
291          Dmsg0(400, "JobMedia record created\n");
292          bnet_fsend(bs, OK_create);
293       }
294
295    } else {
296       omsg = get_memory(bs->msglen+1);
297       pm_strcpy(omsg, bs->msg);
298       bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);
299       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg);
300       free_memory(omsg);
301    }
302    Dmsg1(400, ">CatReq response: %s", bs->msg);
303    Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
304    return;
305 }
306
307 /*
308  * Update File Attributes in the catalog with data
309  *  sent by the Storage daemon.  Note, we receive the whole
310  *  attribute record, but we select out only the stat packet,
311  *  VolSessionId, VolSessionTime, FileIndex, and file name
312  *  to store in the catalog.
313  */
314 void catalog_update(JCR *jcr, BSOCK *bs)
315 {
316    unser_declare;
317    uint32_t VolSessionId, VolSessionTime;
318    int32_t Stream;
319    uint32_t FileIndex;
320    uint32_t data_len;
321    char *p;
322    int len;
323    char *fname, *attr;
324    ATTR_DBR *ar = NULL;
325    POOLMEM *omsg;
326
327    if (!jcr->pool->catalog_files) {
328       return;                         /* user disabled cataloging */
329    }
330    if (!jcr->db) {
331       omsg = get_memory(bs->msglen+1);
332       pm_strcpy(omsg, bs->msg);
333       bnet_fsend(bs, _("1991 Invalid Catalog Update: %s"), omsg);    
334       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog Update; DB not open: %s"), omsg);
335       free_memory(omsg);
336       return;
337    }
338
339    /* Start transaction allocates jcr->attr and jcr->ar if needed */
340    db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
341    ar = jcr->ar;      
342
343    /* Start by scanning directly in the message buffer to get Stream   
344     *  there may be a cached attr so we cannot yet write into
345     *  jcr->attr or jcr->ar  
346     */
347    p = bs->msg;
348    skip_nonspaces(&p);                /* UpdCat */
349    skip_spaces(&p);
350    skip_nonspaces(&p);                /* Job=nnn */
351    skip_spaces(&p);
352    skip_nonspaces(&p);                /* FileAttributes */
353    p += 1;
354    unser_begin(p, 0);
355    unser_uint32(VolSessionId);
356    unser_uint32(VolSessionTime);
357    unser_int32(FileIndex);
358    unser_int32(Stream);
359    unser_uint32(data_len);
360    p += unser_length(p);
361
362    Dmsg1(400, "UpdCat msg=%s\n", bs->msg);
363    Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d data_len=%d\n",
364       VolSessionId, VolSessionTime, FileIndex, Stream, data_len);
365
366    if (Stream == STREAM_UNIX_ATTRIBUTES || Stream == STREAM_UNIX_ATTRIBUTES_EX) {
367       if (jcr->cached_attribute) {
368          Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
369          if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
370             Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
371          }
372       }
373       /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
374       jcr->attr = check_pool_memory_size(jcr->attr, bs->msglen);
375       memcpy(jcr->attr, bs->msg, bs->msglen);
376       p = jcr->attr - bs->msg + p;    /* point p into jcr->attr */
377       skip_nonspaces(&p);             /* skip FileIndex */
378       skip_spaces(&p);
379       skip_nonspaces(&p);             /* skip FileType */
380       skip_spaces(&p);
381       fname = p;
382       len = strlen(fname);        /* length before attributes */
383       attr = &fname[len+1];
384
385       Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
386       Dmsg1(400, "dird<stored: attr=%s\n", attr);
387       ar->attr = attr;
388       ar->fname = fname;
389       ar->FileIndex = FileIndex;
390       ar->Stream = Stream;
391       ar->link = NULL;
392       ar->JobId = jcr->JobId;
393       ar->Sig = NULL;
394       ar->SigType = 0;
395       jcr->cached_attribute = true;
396
397       Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
398       Dmsg1(400, "dird<filed: attr=%s\n", attr);
399
400 #ifdef xxx_old_code
401       if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
402          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
403       }
404 #endif
405    } else if (Stream == STREAM_MD5_SIGNATURE || Stream == STREAM_SHA1_SIGNATURE) {
406       fname = p;
407       if (ar->FileIndex != FileIndex) {
408          Jmsg(jcr, M_WARNING, 0, _("Got MD5/SHA1 but not same File as attributes\n"));
409       } else {
410          /* Update signature in catalog */
411          char SIGbuf[50];           /* 24 bytes should be enough */
412          int len, type;
413          if (Stream == STREAM_MD5_SIGNATURE) {
414             len = 16;
415             type = MD5_SIG;
416          } else {
417             len = 20;
418             type = SHA1_SIG;
419          }
420          bin_to_base64(SIGbuf, fname, len);
421          Dmsg3(400, "SIGlen=%d SIG=%s type=%d\n", strlen(SIGbuf), SIGbuf, Stream);
422          if (jcr->cached_attribute) {
423             ar->Sig = SIGbuf;
424             ar->SigType = type;
425             Dmsg2(400, "Cached attr with SIG. Stream=%d fname=%s\n", ar->Stream, ar->fname);
426             if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
427                Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
428             }
429             jcr->cached_attribute = false; 
430          } else {
431             if (!db_add_SIG_to_file_record(jcr, jcr->db, ar->FileId, SIGbuf, type)) {
432                Jmsg(jcr, M_ERROR, 0, _("Catalog error updating MD5/SHA1. %s"),
433                   db_strerror(jcr->db));
434             }
435          }
436       }
437    }
438 }