3 * Bacula Director -- catreq.c -- handles the message channel
4 * catalog request from the Storage daemon.
6 * Kern Sibbald, March MMI
8 * This routine runs as a thread and must be thread reentrant.
10 * Basic tasks done here:
11 * Handle Catalog services.
16 Copyright (C) 2001-2005 Kern Sibbald
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.
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.
34 * Handle catalog request
35 * For now, we simply return next Volume to be used
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";
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
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";
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";
60 static char OK_create[] = "1000 OK CreateJobMedia\n";
63 static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr)
66 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
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,
78 edit_uint64(mr->VolReadTime, ed4),
79 edit_uint64(mr->VolWriteTime, ed5),
80 mr->EndFile, mr->EndBlock,
83 unbash_spaces(mr->VolumeName);
84 Dmsg2(100, "Vol Info for %s: %s", jcr->Job, sd->msg);
88 void catalog_request(JCR *jcr, BSOCK *bs)
92 char Job[MAX_NAME_LENGTH];
93 char pool_name[MAX_NAME_LENGTH];
94 int index, ok, label, writing;
98 memset(&mr, 0, sizeof(mr));
99 memset(&sdmr, 0, sizeof(sdmr));
100 memset(&jm, 0, sizeof(jm));
103 * Request to find next appendable Volume for this Job
105 Dmsg1(100, "catreq %s", bs->msg);
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);
115 * Find next appendable medium for SD
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);
122 mr.PoolId = pr.PoolId;
123 ok = find_next_volume_for_append(jcr, &mr, true /*permit create new vol*/);
126 * Send Find Media response to Storage daemon
129 send_volume_info_to_storage_daemon(jcr, bs, &mr);
131 bnet_fsend(bs, _("1901 No Media.\n"));
132 Dmsg0(500, "1901 No Media.\n");
136 * Request to find specific Volume information
138 } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing) == 3) {
139 Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName);
143 unbash_spaces(mr.VolumeName);
144 if (db_get_media_record(jcr, jcr->db, &mr)) {
145 const char *reason = NULL; /* detailed reason for rejection */
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
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.
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");
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!
169 * } else if (!jcr->pool->accept_any_volume) {
170 * reason = "Volume not in sequence";
174 * Now try recycling if necessary
175 * reason set non-NULL if we cannot use it
177 check_if_volume_valid_or_recyclable(jcr, &mr, &reason);
180 if (reason == NULL) {
182 * Send Find Media response to Storage daemon
184 send_volume_info_to_storage_daemon(jcr, bs, &mr);
186 /* Not suitable volume */
187 bnet_fsend(bs, _("1998 Volume \"%s\" status is %s, %s.\n"), mr.VolumeName,
188 mr.VolStatus, reason);
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);
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.
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) {
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));
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;
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);
232 * Insanity check for VolFiles get set to a smaller value
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.VolViles);
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;
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));
261 Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
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.
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);
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");
277 * Request to create a JobMedia record
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) {
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"));
292 Dmsg0(400, "JobMedia record created\n");
293 bnet_fsend(bs, OK_create);
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);
303 Dmsg1(400, ">CatReq response: %s", bs->msg);
304 Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
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.
315 void catalog_update(JCR *jcr, BSOCK *bs)
318 uint32_t VolSessionId, VolSessionTime;
328 if (!jcr->pool->catalog_files) {
329 return; /* user disabled cataloging */
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);
340 /* Start transaction allocates jcr->attr and jcr->ar if needed */
341 db_start_transaction(jcr, jcr->db); /* start transaction if not already open */
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
349 skip_nonspaces(&p); /* UpdCat */
351 skip_nonspaces(&p); /* Job=nnn */
353 skip_nonspaces(&p); /* FileAttributes */
356 unser_uint32(VolSessionId);
357 unser_uint32(VolSessionTime);
358 unser_int32(FileIndex);
360 unser_uint32(data_len);
361 p += unser_length(p);
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);
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));
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 */
380 skip_nonspaces(&p); /* skip FileType */
383 len = strlen(fname); /* length before attributes */
384 attr = &fname[len+1];
386 Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
387 Dmsg1(400, "dird<stored: attr=%s\n", attr);
390 ar->FileIndex = FileIndex;
393 ar->JobId = jcr->JobId;
396 jcr->cached_attribute = true;
398 Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
399 Dmsg1(400, "dird<filed: attr=%s\n", attr);
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));
406 } else if (Stream == STREAM_MD5_SIGNATURE || Stream == STREAM_SHA1_SIGNATURE) {
408 if (ar->FileIndex != FileIndex) {
409 Jmsg(jcr, M_WARNING, 0, _("Got MD5/SHA1 but not same File as attributes\n"));
411 /* Update signature in catalog */
412 char SIGbuf[50]; /* 24 bytes should be enough */
414 if (Stream == STREAM_MD5_SIGNATURE) {
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) {
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));
430 jcr->cached_attribute = false;
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));