]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/catreq.c
7099fbe05712751c13d4f9caf7dd734572376cb4
[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) 2000, 2001, 2002 Kern Sibbald and John Walker
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 as
20    published by the Free Software Foundation; either version 2 of
21    the License, or (at your option) any later version.
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 GNU
26    General Public License for more details.
27
28    You should have received a copy of the GNU General Public
29    License along with this program; if not, write to the Free
30    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
31    MA 02111-1307, USA.
32
33  */
34
35 #include "bacula.h"
36 #include "dird.h"
37
38 /*
39  * Handle catalog request
40  *  For now, we simply return next Volume to be used
41  */
42
43 /* Requests from the Storage daemon */
44 static char Find_media[] = "CatReq Job=%127s FindMedia=%d\n";
45 static char Get_Vol_Info[] = "CatReq Job=%127s GetVolInfo VolName=%127s write=%d\n";
46
47 static char Update_media[] = "CatReq Job=%127s UpdateMedia VolName=%s\
48  VolJobs=%d VolFiles=%d VolBlocks=%d VolBytes=%" lld " VolMounts=%d\
49  VolErrors=%d VolWrites=%d VolMaxBytes=%" lld " EndTime=%d VolStatus=%10s\
50  Slot=%d relabel=%d\n";
51
52 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia \
53  FirstIndex=%d LastIndex=%d StartFile=%d EndFile=%d \
54  StartBlock=%d EndBlock=%d\n";
55
56
57 /* Responses  sent to Storage daemon */
58 static char OK_media[] = "1000 OK VolName=%s VolJobs=%d VolFiles=%d\
59  VolBlocks=%d VolBytes=%" lld " VolMounts=%d VolErrors=%d VolWrites=%d\
60  VolMaxBytes=%" lld " VolCapacityBytes=%" lld " VolStatus=%s Slot=%d\n";
61
62 static char OK_update[] = "1000 OK UpdateMedia\n";
63
64 /* static char FileAttributes[] = "UpdCat Job=%127s FileAttributes "; */
65
66
67 void catalog_request(JCR *jcr, BSOCK *bs, char *msg)
68 {
69    MEDIA_DBR mr; 
70    JOBMEDIA_DBR jm;
71    char Job[MAX_NAME_LENGTH];
72    int index, ok, relabel, writing, retry = 0;
73    char *omsg;
74
75    memset(&mr, 0, sizeof(mr));
76    memset(&jm, 0, sizeof(jm));
77
78    /*
79     * Request to find next appendable Volume for this Job
80     */
81    Dmsg1(120, "catreq %s", bs->msg);
82    if (sscanf(bs->msg, Find_media, &Job, &index) == 2) {
83       mr.PoolId = jcr->PoolId;
84       strcpy(mr.MediaType, jcr->store->media_type);
85       Dmsg3(120, "CatReq FindMedia: Id=%d, MediaType=%s, Status=%s\n",
86          mr.PoolId, mr.MediaType, mr.VolStatus);
87       /*
88        * Find the Next Volume for Append
89        */
90 next_volume:
91       strcpy(mr.VolStatus, "Append");  /* want only appendable volumes */
92       ok = db_find_next_volume(jcr->db, index, &mr);  
93       if (!ok) {
94          /* Well, try finding recycled tapes */
95          ok = find_recycled_volume(jcr, &mr);
96          Dmsg1(100, "find_recycled_volume1 %d\n", ok);
97          if (!ok) {
98             prune_volumes(jcr);  
99             ok = recycle_a_volume(jcr, &mr);
100             Dmsg1(100, "find_recycled_volume2 %d\n", ok);
101             if (!ok) {
102                /* See if we can create a new Volume */
103                ok = newVolume(jcr, &mr);
104             }
105          }
106       }
107       /* Check if use duration has expired */
108       if (ok && jcr->pool->VolUseDuration > 0 && 
109                 strcmp(mr.VolStatus, "Recycle") != 0) {
110          utime_t now = time(NULL);
111          utime_t start = str_to_utime(mr.cFirstWritten);                         
112          if (start > 0 && jcr->pool->VolUseDuration <= (now - start)) {
113             Dmsg4(100, "Duration=%d now=%d start=%d now-start=%d\n",
114                (int)jcr->pool->VolUseDuration, (int)now, (int)start, (int)(now-start));
115             Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "       
116                "Marking Volume \"%s\" as Used.\n"), mr.VolumeName);
117             strcpy(mr.VolStatus, "Used");  /* yes, mark as used */
118             if (!db_update_media_record(jcr->db, &mr)) {
119                Jmsg(jcr, M_ERROR, 0, _("Catalog error updating Media record. %s"),
120                     db_strerror(jcr->db));
121             } else if (retry++ < 200) {     /* sanity check */
122                goto next_volume;
123             } else {
124                Jmsg(jcr, M_ERROR, 0, _(
125 "We seem to be looping trying to find the next volume. I give up. Ask operator.\n"));
126             }
127             ok = FALSE;               /* give up */
128          }
129       }
130
131       /*
132        * Send Find Media response to Storage daemon 
133        */
134       if (ok) {
135          jcr->MediaId = mr.MediaId;
136          strcpy(jcr->VolumeName, mr.VolumeName);
137          bash_spaces(mr.VolumeName);
138          bnet_fsend(bs, OK_media, mr.VolumeName, mr.VolJobs,
139             mr.VolFiles, mr.VolBlocks, mr.VolBytes, mr.VolMounts, mr.VolErrors,
140             mr.VolWrites, mr.VolMaxBytes, mr.VolCapacityBytes,
141             mr.VolStatus, mr.Slot);
142       } else {
143          bnet_fsend(bs, "1999 No Media\n");
144       }
145
146    /* 
147     * Request to find specific Volume information
148     */
149    } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing) == 3) {
150       Dmsg1(120, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName);
151       /*
152        * Find the Volume
153        */
154       unbash_spaces(mr.VolumeName);
155       if (db_get_media_record(jcr->db, &mr)) {
156          int VolSuitable = 0;
157          jcr->MediaId = mr.MediaId;
158          Dmsg1(120, "VolumeInfo MediaId=%d\n", jcr->MediaId);
159          strcpy(jcr->VolumeName, mr.VolumeName);
160          if (!writing) {
161             VolSuitable = 1;          /* accept anything for read */
162          } else {
163             /* 
164              * Make sure this volume is suitable for this job, i.e.
165              *  it is either Append or Recycle and Media Type matches 
166              *  and Pool allows any volume.
167              */
168             if (mr.PoolId == jcr->PoolId && 
169                 (strcmp(mr.VolStatus, "Append") == 0 ||
170                  strcmp(mr.VolStatus, "Recycle") == 0) &&
171                  strcmp(mr.MediaType, jcr->store->media_type) == 0 &&
172                  jcr->pool->accept_any_volume) {
173                VolSuitable = 1;
174             }
175          }
176          if (VolSuitable) {
177             /*
178              * Send Find Media response to Storage daemon 
179              */
180             bash_spaces(mr.VolumeName);
181             bnet_fsend(bs, OK_media, mr.VolumeName, mr.VolJobs,
182                mr.VolFiles, mr.VolBlocks, mr.VolBytes, mr.VolMounts, mr.VolErrors,
183                mr.VolWrites, mr.VolMaxBytes, mr.VolCapacityBytes,
184                mr.VolStatus, mr.Slot);
185             Dmsg5(200, "get_media_record PoolId=%d wanted %d, Status=%s, Slot=%d \
186 MediaType=%s\n", mr.PoolId, jcr->PoolId, mr.VolStatus, mr.Slot, mr.MediaType);
187          } else { 
188             /* Not suitable volume */
189             bnet_fsend(bs, "1998 Volume not appropriate.\n");
190          }
191
192       } else {
193          bnet_fsend(bs, "1999 Volume Not Found.\n");
194       }
195
196    
197    /*
198     * Request to update Media record. Comes typically at the end
199     *  of a Storage daemon Job Session
200     */
201    } else if (sscanf(bs->msg, Update_media, &Job, &mr.VolumeName, &mr.VolJobs,
202       &mr.VolFiles, &mr.VolBlocks, &mr.VolBytes, &mr.VolMounts, &mr.VolErrors,
203       &mr.VolWrites, &mr.VolMaxBytes, &mr.LastWritten, &mr.VolStatus, 
204       &mr.Slot, &relabel) == 14) {
205
206       /*     
207        * Update Media Record
208        */
209
210       /* First handle Max Volume Bytes */
211       if ((mr.VolMaxBytes > 0 && mr.VolBytes >= mr.VolMaxBytes)) {
212          Jmsg(jcr, M_INFO, 0, _("Max configure Volume bytes exceeded. "             
213              "Marking Volume \"%s\" as Full.\n"), mr.VolumeName);
214          strcpy(mr.VolStatus, "Full");
215
216       /* Now see if Volume should only be used once */
217       } else if (mr.VolBytes > 0 && jcr->pool->use_volume_once) {
218          Jmsg(jcr, M_INFO, 0, _("Volume used once. As configured, "             
219              "marking Volume \"%s\" as Used.\n"), mr.VolumeName);
220          strcpy(mr.VolStatus, "Used");
221
222       /* Now see if Max Jobs written to volume */
223       } else if (jcr->pool->MaxVolumeJobs > 0 &&
224                  jcr->pool->MaxVolumeJobs <= mr.VolJobs) {
225          Jmsg(jcr, M_INFO, 0, _("Max configured Volume jobs exceeded. "       
226              "Marking Volume \"%s\" as Used.\n"), mr.VolumeName);
227          strcpy(mr.VolStatus, "Used");
228
229       /* Finally, check Use duration expiration */
230       } else if (jcr->pool->VolUseDuration > 0) {
231          MEDIA_DBR omr;
232          memset(&omr, 0, sizeof(omr));   /* clear media record */
233          strcpy(omr.VolumeName, mr.VolumeName); /* copy Volume name */
234          if (db_get_media_record(jcr->db, &omr)) {
235             utime_t start;
236             utime_t now = time(NULL);
237             start = str_to_utime(omr.cFirstWritten);                         
238             /* See if Vol Use has expired */
239             if (start > 0 && jcr->pool->VolUseDuration <= (now - start)) {
240                Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "       
241                   "Marking Volume \"%s\"as Used.\n"), mr.VolumeName);
242                strcpy(mr.VolStatus, "Used");  /* yes, mark as used */
243             }
244          } else {
245             Jmsg(jcr, M_ERROR, 0, _("Unable to get Media record for Volume %s: ERR=%s\n"),
246                mr.VolumeName, db_strerror(jcr->db));
247          }
248       }
249
250       Dmsg2(200, "db_update_media_record. Stat=%s Vol=%s\n",
251          mr.VolStatus, mr.VolumeName);
252       if (db_update_media_record(jcr->db, &mr)) {
253          bnet_fsend(bs, OK_update);
254          Dmsg0(190, "send OK\n");
255       } else {
256          Jmsg(jcr, M_ERROR, 0, _("Catalog error updating Media record. %s"),
257             db_strerror(jcr->db));
258          bnet_fsend(bs, "1992 Update Media error\n");
259          Dmsg0(190, "send error\n");
260       }
261
262    /*
263     * Request to create a JobMedia record
264     */
265    } else if (sscanf(bs->msg, Create_job_media, &Job,
266       &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile,
267       &jm.StartBlock, &jm.EndBlock) == 7) {
268
269       jm.JobId = jcr->JobId;
270       jm.MediaId = jcr->MediaId;
271       Dmsg6(100, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n",
272          jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex);
273       if(!db_create_jobmedia_record(jcr->db, &jm)) {
274          Jmsg(jcr, M_ERROR, 0, _("Catalog error creating JobMedia record. %s"),
275             db_strerror(jcr->db));
276          bnet_fsend(bs, "1991 Update JobMedia error\n");
277       } else {
278          Dmsg0(100, "JobMedia record created\n");
279          bnet_fsend(bs, OK_update);
280       }
281
282    } else {
283       omsg = (char *) get_memory(bs->msglen+1);
284       strcpy(omsg, bs->msg);
285       bnet_fsend(bs, "1990 Invalid Catalog Request: %s", omsg);    
286       free_memory(omsg);
287    }
288    Dmsg1(120, ">CatReq response: %s", bs->msg);
289    return;
290 }
291
292 /*
293  * Update File Attributes in the catalog with data
294  *  sent by the Storage daemon.
295  */
296 void catalog_update(JCR *jcr, BSOCK *bs, char *msg)
297 {
298    unser_declare;
299    uint32_t VolSessionId, VolSessionTime;
300    int32_t Stream;
301    uint32_t FileIndex;
302    uint32_t data_len;
303    char *p = bs->msg;
304    int len;
305    char *fname, *attr;
306    ATTR_DBR ar;
307
308    if (!jcr->pool->catalog_files) {
309       return;
310    }
311    db_start_transaction(jcr->db);     /* start transaction if not already open */
312    skip_nonspaces(&p);                /* UpdCat */
313    skip_spaces(&p);
314    skip_nonspaces(&p);                /* Job=nnn */
315    skip_spaces(&p);
316    skip_nonspaces(&p);                /* FileAttributes */
317    p += 1;
318    unser_begin(p, 0);
319    unser_uint32(VolSessionId);
320    unser_uint32(VolSessionTime);
321    unser_int32(FileIndex);
322    unser_int32(Stream);
323    unser_uint32(data_len);
324    p += unser_length(p);
325
326    Dmsg1(99, "UpdCat msg=%s\n", bs->msg);
327    Dmsg5(99, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d data_len=%d\n",
328       VolSessionId, VolSessionTime, FileIndex, Stream, data_len);
329
330    if (Stream == STREAM_UNIX_ATTRIBUTES || Stream == STREAM_WIN32_ATTRIBUTES) {
331       skip_nonspaces(&p);             /* skip FileIndex */
332       skip_spaces(&p);
333       skip_nonspaces(&p);             /* skip FileType */
334       skip_spaces(&p);
335       fname = p;
336       len = strlen(fname);        /* length before attributes */
337       attr = &fname[len+1];
338
339       Dmsg2(109, "dird<stored: stream=%d %s\n", Stream, fname);
340       Dmsg1(109, "dird<stored: attr=%s\n", attr);
341       ar.attr = attr; 
342       ar.fname = fname;
343       ar.FileIndex = FileIndex;
344       ar.Stream = Stream;
345       ar.link = NULL;
346       ar.JobId = jcr->JobId;
347
348       Dmsg2(111, "dird<filed: stream=%d %s\n", Stream, fname);
349       Dmsg1(120, "dird<filed: attr=%s\n", attr);
350
351       if (!db_create_file_attributes_record(jcr->db, &ar)) {
352          Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
353       }
354       /* Save values for MD5 update */
355       jcr->FileId = ar.FileId;
356       jcr->FileIndex = FileIndex;
357    } else if (Stream == STREAM_MD5_SIGNATURE) {
358       fname = p;
359       if (jcr->FileIndex != FileIndex) {    
360          Jmsg(jcr, M_WARNING, 0, "Got MD5 but not same File as attributes\n");
361       } else {
362          /* Update MD5 signature in catalog */
363          char MD5buf[50];           /* 24 bytes should be enough */
364          bin_to_base64(MD5buf, fname, 16);
365          Dmsg2(190, "MD5len=%d MD5=%s\n", strlen(MD5buf), MD5buf);
366          if (!db_add_MD5_to_file_record(jcr->db, jcr->FileId, MD5buf)) {
367             Jmsg(jcr, M_ERROR, 0, _("Catalog error updating MD5. %s"), 
368                db_strerror(jcr->db));
369          }
370       }
371    }
372 }