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-2006 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.
32 #include "findlib/find.h"
35 * Handle catalog request
36 * For now, we simply return next Volume to be used
39 /* Requests from the Storage daemon */
40 static char Find_media[] = "CatReq Job=%127s FindMedia=%d pool_name=%127s media_type=%127s\n";
41 static char Get_Vol_Info[] = "CatReq Job=%127s GetVolInfo VolName=%127s write=%d\n";
43 static char Update_media[] = "CatReq Job=%127s UpdateMedia VolName=%s"
44 " VolJobs=%u VolFiles=%u VolBlocks=%u VolBytes=%" lld " VolMounts=%u"
45 " VolErrors=%u VolWrites=%u MaxVolBytes=%" lld " EndTime=%d VolStatus=%10s"
46 " Slot=%d relabel=%d InChanger=%d VolReadTime=%" lld " VolWriteTime=%" lld
49 static char Create_job_media[] = "CatReq Job=%127s CreateJobMedia "
50 " FirstIndex=%u LastIndex=%u StartFile=%u EndFile=%u "
51 " StartBlock=%u EndBlock=%u Copy=%d Strip=%d\n";
54 /* Responses sent to Storage daemon */
55 static char OK_media[] = "1000 OK VolName=%s VolJobs=%u VolFiles=%u"
56 " VolBlocks=%u VolBytes=%s VolMounts=%u VolErrors=%u VolWrites=%u"
57 " MaxVolBytes=%s VolCapacityBytes=%s VolStatus=%s Slot=%d"
58 " MaxVolJobs=%u MaxVolFiles=%u InChanger=%d VolReadTime=%s"
59 " VolWriteTime=%s EndFile=%u EndBlock=%u VolParts=%u LabelType=%d\n";
61 static char OK_create[] = "1000 OK CreateJobMedia\n";
64 static int send_volume_info_to_storage_daemon(JCR *jcr, BSOCK *sd, MEDIA_DBR *mr)
67 char ed1[50], ed2[50], ed3[50], ed4[50], ed5[50];
69 jcr->MediaId = mr->MediaId;
70 pm_strcpy(jcr->VolumeName, mr->VolumeName);
71 bash_spaces(mr->VolumeName);
72 stat = bnet_fsend(sd, OK_media, mr->VolumeName, mr->VolJobs,
73 mr->VolFiles, mr->VolBlocks, edit_uint64(mr->VolBytes, ed1),
74 mr->VolMounts, mr->VolErrors, mr->VolWrites,
75 edit_uint64(mr->MaxVolBytes, ed2),
76 edit_uint64(mr->VolCapacityBytes, ed3),
77 mr->VolStatus, mr->Slot, mr->MaxVolJobs, mr->MaxVolFiles,
79 edit_uint64(mr->VolReadTime, ed4),
80 edit_uint64(mr->VolWriteTime, ed5),
81 mr->EndFile, mr->EndBlock,
84 unbash_spaces(mr->VolumeName);
85 Dmsg2(100, "Vol Info for %s: %s", jcr->Job, sd->msg);
89 void catalog_request(JCR *jcr, BSOCK *bs)
93 char Job[MAX_NAME_LENGTH];
94 char pool_name[MAX_NAME_LENGTH];
95 int index, ok, label, writing;
99 memset(&mr, 0, sizeof(mr));
100 memset(&sdmr, 0, sizeof(sdmr));
101 memset(&jm, 0, sizeof(jm));
104 * Request to find next appendable Volume for this Job
106 Dmsg1(100, "catreq %s", bs->msg);
108 omsg = get_memory(bs->msglen+1);
109 pm_strcpy(omsg, bs->msg);
110 bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);
111 Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request; DB not open: %s"), omsg);
116 * Find next appendable medium for SD
118 if (sscanf(bs->msg, Find_media, &Job, &index, &pool_name, &mr.MediaType) == 4) {
119 memset(&pr, 0, sizeof(pr));
120 bstrncpy(pr.Name, pool_name, sizeof(pr.Name));
121 unbash_spaces(pr.Name);
122 ok = db_get_pool_record(jcr, jcr->db, &pr);
124 mr.PoolId = pr.PoolId;
125 mr.StorageId = jcr->store->StorageId;
126 ok = find_next_volume_for_append(jcr, &mr, index, true /*permit create new vol*/);
127 Dmsg3(100, "find_media idx=%d ok=%d vol=%s\n", index, ok, mr.VolumeName);
130 * Send Find Media response to Storage daemon
133 send_volume_info_to_storage_daemon(jcr, bs, &mr);
135 bnet_fsend(bs, _("1901 No Media.\n"));
136 Dmsg0(500, "1901 No Media.\n");
140 * Request to find specific Volume information
142 } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing) == 3) {
143 Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName);
147 unbash_spaces(mr.VolumeName);
148 if (db_get_media_record(jcr, jcr->db, &mr)) {
149 const char *reason = NULL; /* detailed reason for rejection */
151 * If we are reading, accept any volume (reason == NULL)
152 * If we are writing, check if the Volume is valid
153 * for this job, and do a recycle if necessary
157 * SD wants to write this Volume, so make
158 * sure it is suitable for this job, i.e.
159 * Pool matches, and it is either Append or Recycle
160 * and Media Type matches and Pool allows any volume.
162 if (mr.PoolId != jcr->jr.PoolId) {
163 reason = _("not in Pool");
164 } else if (strcmp(mr.MediaType, jcr->store->media_type) != 0) {
165 reason = _("not correct MediaType");
168 * Now try recycling if necessary
169 * reason set non-NULL if we cannot use it
171 check_if_volume_valid_or_recyclable(jcr, &mr, &reason);
174 if (reason == NULL) {
176 * Send Find Media response to Storage daemon
178 send_volume_info_to_storage_daemon(jcr, bs, &mr);
180 /* Not suitable volume */
181 bnet_fsend(bs, _("1998 Volume \"%s\" status is %s, %s.\n"), mr.VolumeName,
182 mr.VolStatus, reason);
186 bnet_fsend(bs, _("1997 Volume \"%s\" not in catalog.\n"), mr.VolumeName);
187 Dmsg1(100, "1997 Volume \"%s\" not in catalog.\n", mr.VolumeName);
191 * Request to update Media record. Comes typically at the end
192 * of a Storage daemon Job Session, when labeling/relabeling a
193 * Volume, or when an EOF mark is written.
195 } else if (sscanf(bs->msg, Update_media, &Job, &sdmr.VolumeName,
196 &sdmr.VolJobs, &sdmr.VolFiles, &sdmr.VolBlocks, &sdmr.VolBytes,
197 &sdmr.VolMounts, &sdmr.VolErrors, &sdmr.VolWrites, &sdmr.MaxVolBytes,
198 &sdmr.LastWritten, &sdmr.VolStatus, &sdmr.Slot, &label, &sdmr.InChanger,
199 &sdmr.VolReadTime, &sdmr.VolWriteTime, &sdmr.VolParts) == 18) {
202 Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName,
203 mr.VolStatus, sdmr.VolStatus);
204 bstrncpy(mr.VolumeName, sdmr.VolumeName, sizeof(mr.VolumeName)); /* copy Volume name */
205 unbash_spaces(mr.VolumeName);
206 if (!db_get_media_record(jcr, jcr->db, &mr)) {
207 Jmsg(jcr, M_ERROR, 0, _("Unable to get Media record for Volume %s: ERR=%s\n"),
208 mr.VolumeName, db_strerror(jcr->db));
209 bnet_fsend(bs, _("1991 Catalog Request for vol=%s failed: %s"),
210 mr.VolumeName, db_strerror(jcr->db));
214 /* Set first written time if this is first job */
215 if (mr.FirstWritten == 0) {
216 mr.FirstWritten = jcr->start_time; /* use Job start time as first write */
217 mr.set_first_written = true;
219 /* If we just labeled the tape set time */
220 if (label || mr.LabelDate == 0) {
221 mr.LabelDate = jcr->start_time;
222 mr.set_label_date = true;
223 Dmsg2(400, "label=%d labeldate=%d\n", label, mr.LabelDate);
226 * Insanity check for VolFiles get set to a smaller value
228 if (sdmr.VolFiles < mr.VolFiles) {
229 Jmsg(jcr, M_FATAL, 0, _("Volume Files at %u being set to %u"
230 " for Volume \"%s\". This is incorrect.\n"),
231 mr.VolFiles, sdmr.VolFiles, mr.VolumeName);
232 bnet_fsend(bs, _("1992 Update Media error. VolFiles=%u, CatFiles=%u\n"),
233 sdmr.VolFiles, mr.VolFiles);
238 Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs);
239 /* Copy updated values to original media record */
240 mr.VolJobs = sdmr.VolJobs;
241 mr.VolFiles = sdmr.VolFiles;
242 mr.VolBlocks = sdmr.VolBlocks;
243 mr.VolBytes = sdmr.VolBytes;
244 mr.VolMounts = sdmr.VolMounts;
245 mr.VolErrors = sdmr.VolErrors;
246 mr.VolWrites = sdmr.VolWrites;
247 mr.LastWritten = sdmr.LastWritten;
249 mr.InChanger = sdmr.InChanger;
250 mr.VolReadTime = sdmr.VolReadTime;
251 mr.VolWriteTime = sdmr.VolWriteTime;
252 mr.VolParts = sdmr.VolParts;
253 bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
254 if (jcr->store->StorageId) {
255 mr.StorageId = jcr->store->StorageId;
258 Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
260 * Update the database, then before sending the response to the
261 * SD, check if the Volume has expired.
263 if (!db_update_media_record(jcr, jcr->db, &mr)) {
264 Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"),
265 db_strerror(jcr->db));
266 bnet_fsend(bs, _("1993 Update Media error\n"));
267 Dmsg0(400, "send error\n");
269 (void)has_volume_expired(jcr, &mr);
270 send_volume_info_to_storage_daemon(jcr, bs, &mr);
275 * Request to create a JobMedia record
277 } else if (sscanf(bs->msg, Create_job_media, &Job,
278 &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile,
279 &jm.StartBlock, &jm.EndBlock, &jm.Copy, &jm.Stripe) == 9) {
281 if (jcr->previous_jcr) {
282 jm.JobId = jcr->previous_jcr->JobId;
283 jm.MediaId = jcr->MediaId;
285 jm.JobId = jcr->JobId;
286 jm.MediaId = jcr->MediaId;
288 Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n",
289 jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex);
290 if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) {
291 Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"),
292 db_strerror(jcr->db));
293 bnet_fsend(bs, _("1991 Update JobMedia error\n"));
295 Dmsg0(400, "JobMedia record created\n");
296 bnet_fsend(bs, OK_create);
300 omsg = get_memory(bs->msglen+1);
301 pm_strcpy(omsg, bs->msg);
302 bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);
303 Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg);
306 Dmsg1(400, ">CatReq response: %s", bs->msg);
307 Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
312 * Update File Attributes in the catalog with data
313 * sent by the Storage daemon. Note, we receive the whole
314 * attribute record, but we select out only the stat packet,
315 * VolSessionId, VolSessionTime, FileIndex, and file name
316 * to store in the catalog.
318 void catalog_update(JCR *jcr, BSOCK *bs)
321 uint32_t VolSessionId, VolSessionTime;
331 if (!jcr->pool->catalog_files) {
332 return; /* user disabled cataloging */
335 omsg = get_memory(bs->msglen+1);
336 pm_strcpy(omsg, bs->msg);
337 bnet_fsend(bs, _("1991 Invalid Catalog Update: %s"), omsg);
338 Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog Update; DB not open: %s"), omsg);
343 /* Start transaction allocates jcr->attr and jcr->ar if needed */
344 db_start_transaction(jcr, jcr->db); /* start transaction if not already open */
347 /* Start by scanning directly in the message buffer to get Stream
348 * there may be a cached attr so we cannot yet write into
349 * jcr->attr or jcr->ar
352 skip_nonspaces(&p); /* UpdCat */
354 skip_nonspaces(&p); /* Job=nnn */
356 skip_nonspaces(&p); /* FileAttributes */
359 unser_uint32(VolSessionId);
360 unser_uint32(VolSessionTime);
361 unser_int32(FileIndex);
363 unser_uint32(data_len);
364 p += unser_length(p);
366 Dmsg1(400, "UpdCat msg=%s\n", bs->msg);
367 Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d data_len=%d\n",
368 VolSessionId, VolSessionTime, FileIndex, Stream, data_len);
370 if (Stream == STREAM_UNIX_ATTRIBUTES || Stream == STREAM_UNIX_ATTRIBUTES_EX) {
371 if (jcr->cached_attribute) {
372 Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
373 if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
374 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
377 /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
378 jcr->attr = check_pool_memory_size(jcr->attr, bs->msglen);
379 memcpy(jcr->attr, bs->msg, bs->msglen);
380 p = jcr->attr - bs->msg + p; /* point p into jcr->attr */
381 skip_nonspaces(&p); /* skip FileIndex */
383 skip_nonspaces(&p); /* skip FileType */
386 len = strlen(fname); /* length before attributes */
387 attr = &fname[len+1];
389 Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
390 Dmsg1(400, "dird<stored: attr=%s\n", attr);
393 ar->FileIndex = FileIndex;
396 if (jcr->previous_jcr) {
397 ar->JobId = jcr->previous_jcr->JobId;
399 ar->JobId = jcr->JobId;
402 ar->DigestType = CRYPTO_DIGEST_NONE;
403 jcr->cached_attribute = true;
405 Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
406 Dmsg1(400, "dird<filed: attr=%s\n", attr);
408 } else if (crypto_digest_stream_type(Stream) != CRYPTO_DIGEST_NONE) {
410 if (ar->FileIndex != FileIndex) {
411 Jmsg(jcr, M_WARNING, 0, _("Got %s but not same File as attributes\n"), stream_to_ascii(Stream));
413 /* Update digest in catalog */
414 char digestbuf[CRYPTO_DIGEST_MAX_SIZE];
416 int type = CRYPTO_DIGEST_NONE;
419 case STREAM_MD5_DIGEST:
420 len = CRYPTO_DIGEST_MD5_SIZE;
421 type = CRYPTO_DIGEST_MD5;
423 case STREAM_SHA1_DIGEST:
424 len = CRYPTO_DIGEST_SHA1_SIZE;
425 type = CRYPTO_DIGEST_SHA1;
427 case STREAM_SHA256_DIGEST:
428 len = CRYPTO_DIGEST_SHA256_SIZE;
429 type = CRYPTO_DIGEST_SHA256;
431 case STREAM_SHA512_DIGEST:
432 len = CRYPTO_DIGEST_SHA512_SIZE;
433 type = CRYPTO_DIGEST_SHA512;
436 /* Never reached ... */
437 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. Unsupported digest stream type: %d"),
441 bin_to_base64(digestbuf, fname, len);
442 Dmsg3(400, "DigestLen=%d Digest=%s type=%d\n", strlen(digestbuf), digestbuf, Stream);
443 if (jcr->cached_attribute) {
444 ar->Digest = digestbuf;
445 ar->DigestType = type;
446 Dmsg2(400, "Cached attr with digest. Stream=%d fname=%s\n", ar->Stream, ar->fname);
447 if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
448 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
450 jcr->cached_attribute = false;
452 if (!db_add_digest_to_file_record(jcr, jcr->db, ar->FileId, digestbuf, type)) {
453 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating file digest. %s"),
454 db_strerror(jcr->db));