]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/catreq.c
This commit was manufactured by cvs2svn to create tag
[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       unbash_spaces(pr.Name);
121       ok = db_get_pool_record(jcr, jcr->db, &pr);
122       if (ok) {
123          mr.PoolId = pr.PoolId;
124          mr.StorageId = jcr->store->StorageId;
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->Sig = NULL;
391       ar->SigType = 0;
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 (Stream == STREAM_MD5_SIGNATURE || Stream == STREAM_SHA1_SIGNATURE) {
403       fname = p;
404       if (ar->FileIndex != FileIndex) {
405          Jmsg(jcr, M_WARNING, 0, _("Got MD5/SHA1 but not same File as attributes\n"));
406       } else {
407          /* Update signature in catalog */
408          char SIGbuf[50];           /* 24 bytes should be enough */
409          int len, type;
410          if (Stream == STREAM_MD5_SIGNATURE) {
411             len = 16;
412             type = MD5_SIG;
413          } else {
414             len = 20;
415             type = SHA1_SIG;
416          }
417          bin_to_base64(SIGbuf, fname, len);
418          Dmsg3(400, "SIGlen=%d SIG=%s type=%d\n", strlen(SIGbuf), SIGbuf, Stream);
419          if (jcr->cached_attribute) {
420             ar->Sig = SIGbuf;
421             ar->SigType = type;
422             Dmsg2(400, "Cached attr with SIG. Stream=%d fname=%s\n", ar->Stream, ar->fname);
423             if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
424                Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
425             }
426             jcr->cached_attribute = false; 
427          } else {
428             if (!db_add_SIG_to_file_record(jcr, jcr->db, ar->FileId, SIGbuf, type)) {
429                Jmsg(jcr, M_ERROR, 0, _("Catalog error updating MD5/SHA1. %s"),
430                   db_strerror(jcr->db));
431             }
432          }
433       }
434    }
435 }