]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/catreq.c
- Add extra debug to Update media error for VolFiles decrease
[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. VolFiles=%u, CatFiles=%u\n"),
239                sdmr.VolFiles, mr.VolFiles);
240             db_unlock(jcr->db);
241             return;
242          }
243       }
244       Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs);
245       /* Copy updated values to original media record */
246       mr.VolJobs      = sdmr.VolJobs;
247       mr.VolFiles     = sdmr.VolFiles;
248       mr.VolBlocks    = sdmr.VolBlocks;
249       mr.VolBytes     = sdmr.VolBytes;
250       mr.VolMounts    = sdmr.VolMounts;
251       mr.VolErrors    = sdmr.VolErrors;
252       mr.VolWrites    = sdmr.VolWrites;
253       mr.LastWritten  = sdmr.LastWritten;
254       mr.Slot         = sdmr.Slot;
255       mr.InChanger    = sdmr.InChanger;
256       mr.VolReadTime  = sdmr.VolReadTime;
257       mr.VolWriteTime = sdmr.VolWriteTime;
258       mr.VolParts     = sdmr.VolParts;
259       bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
260
261       Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
262       /*
263        * Check if it has expired, and if not update the DB. Note, if
264        *   Volume has expired, has_volume_expired() will update the DB.
265        */
266       if (has_volume_expired(jcr, &mr) || db_update_media_record(jcr, jcr->db, &mr)) {
267          send_volume_info_to_storage_daemon(jcr, bs, &mr);
268       } else {
269          Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"),
270             db_strerror(jcr->db));
271          bnet_fsend(bs, _("1993 Update Media error\n"));
272          Dmsg0(400, "send error\n");
273       }
274       db_unlock(jcr->db);
275
276    /*
277     * Request to create a JobMedia record
278     */
279    } else if (sscanf(bs->msg, Create_job_media, &Job,
280       &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile,
281       &jm.StartBlock, &jm.EndBlock, &jm.Copy, &jm.Stripe) == 9) {
282
283       jm.JobId = jcr->JobId;
284       jm.MediaId = jcr->MediaId;
285       Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n",
286          jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex);
287       if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) {
288          Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"),
289             db_strerror(jcr->db));
290          bnet_fsend(bs, _("1991 Update JobMedia error\n"));
291       } else {
292          Dmsg0(400, "JobMedia record created\n");
293          bnet_fsend(bs, OK_create);
294       }
295
296    } else {
297       omsg = get_memory(bs->msglen+1);
298       pm_strcpy(omsg, bs->msg);
299       bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);
300       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg);
301       free_memory(omsg);
302    }
303    Dmsg1(400, ">CatReq response: %s", bs->msg);
304    Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
305    return;
306 }
307
308 /*
309  * Update File Attributes in the catalog with data
310  *  sent by the Storage daemon.  Note, we receive the whole
311  *  attribute record, but we select out only the stat packet,
312  *  VolSessionId, VolSessionTime, FileIndex, and file name
313  *  to store in the catalog.
314  */
315 void catalog_update(JCR *jcr, BSOCK *bs)
316 {
317    unser_declare;
318    uint32_t VolSessionId, VolSessionTime;
319    int32_t Stream;
320    uint32_t FileIndex;
321    uint32_t data_len;
322    char *p;
323    int len;
324    char *fname, *attr;
325    ATTR_DBR *ar = NULL;
326    POOLMEM *omsg;
327
328    if (!jcr->pool->catalog_files) {
329       return;                         /* user disabled cataloging */
330    }
331    if (!jcr->db) {
332       omsg = get_memory(bs->msglen+1);
333       pm_strcpy(omsg, bs->msg);
334       bnet_fsend(bs, _("1991 Invalid Catalog Update: %s"), omsg);    
335       Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog Update; DB not open: %s"), omsg);
336       free_memory(omsg);
337       return;
338    }
339
340    /* Start transaction allocates jcr->attr and jcr->ar if needed */
341    db_start_transaction(jcr, jcr->db);     /* start transaction if not already open */
342    ar = jcr->ar;      
343
344    /* Start by scanning directly in the message buffer to get Stream   
345     *  there may be a cached attr so we cannot yet write into
346     *  jcr->attr or jcr->ar  
347     */
348    p = bs->msg;
349    skip_nonspaces(&p);                /* UpdCat */
350    skip_spaces(&p);
351    skip_nonspaces(&p);                /* Job=nnn */
352    skip_spaces(&p);
353    skip_nonspaces(&p);                /* FileAttributes */
354    p += 1;
355    unser_begin(p, 0);
356    unser_uint32(VolSessionId);
357    unser_uint32(VolSessionTime);
358    unser_int32(FileIndex);
359    unser_int32(Stream);
360    unser_uint32(data_len);
361    p += unser_length(p);
362
363    Dmsg1(400, "UpdCat msg=%s\n", bs->msg);
364    Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d data_len=%d\n",
365       VolSessionId, VolSessionTime, FileIndex, Stream, data_len);
366
367    if (Stream == STREAM_UNIX_ATTRIBUTES || Stream == STREAM_UNIX_ATTRIBUTES_EX) {
368       if (jcr->cached_attribute) {
369          Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
370          if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
371             Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
372          }
373       }
374       /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
375       jcr->attr = check_pool_memory_size(jcr->attr, bs->msglen);
376       memcpy(jcr->attr, bs->msg, bs->msglen);
377       p = jcr->attr - bs->msg + p;    /* point p into jcr->attr */
378       skip_nonspaces(&p);             /* skip FileIndex */
379       skip_spaces(&p);
380       skip_nonspaces(&p);             /* skip FileType */
381       skip_spaces(&p);
382       fname = p;
383       len = strlen(fname);        /* length before attributes */
384       attr = &fname[len+1];
385
386       Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
387       Dmsg1(400, "dird<stored: attr=%s\n", attr);
388       ar->attr = attr;
389       ar->fname = fname;
390       ar->FileIndex = FileIndex;
391       ar->Stream = Stream;
392       ar->link = NULL;
393       ar->JobId = jcr->JobId;
394       ar->Sig = NULL;
395       ar->SigType = 0;
396       jcr->cached_attribute = true;
397
398       Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
399       Dmsg1(400, "dird<filed: attr=%s\n", attr);
400
401 #ifdef xxx_old_code
402       if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
403          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
404       }
405 #endif
406    } else if (Stream == STREAM_MD5_SIGNATURE || Stream == STREAM_SHA1_SIGNATURE) {
407       fname = p;
408       if (ar->FileIndex != FileIndex) {
409          Jmsg(jcr, M_WARNING, 0, _("Got MD5/SHA1 but not same File as attributes\n"));
410       } else {
411          /* Update signature in catalog */
412          char SIGbuf[50];           /* 24 bytes should be enough */
413          int len, type;
414          if (Stream == STREAM_MD5_SIGNATURE) {
415             len = 16;
416             type = MD5_SIG;
417          } else {
418             len = 20;
419             type = SHA1_SIG;
420          }
421          bin_to_base64(SIGbuf, fname, len);
422          Dmsg3(400, "SIGlen=%d SIG=%s type=%d\n", strlen(SIGbuf), SIGbuf, Stream);
423          if (jcr->cached_attribute) {
424             ar->Sig = SIGbuf;
425             ar->SigType = type;
426             Dmsg2(400, "Cached attr with SIG. Stream=%d fname=%s\n", ar->Stream, ar->fname);
427             if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
428                Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
429             }
430             jcr->cached_attribute = false; 
431          } else {
432             if (!db_add_SIG_to_file_record(jcr, jcr->db, ar->FileId, SIGbuf, type)) {
433                Jmsg(jcr, M_ERROR, 0, _("Catalog error updating MD5/SHA1. %s"),
434                   db_strerror(jcr->db));
435             }
436          }
437       }
438    }
439 }