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\n"));
243 Dmsg2(400, "Update media: BefVolJobs=%u After=%u\n", mr.VolJobs, sdmr.VolJobs);
244 /* Copy updated values to original media record */
245 mr.VolJobs = sdmr.VolJobs;
246 mr.VolFiles = sdmr.VolFiles;
247 mr.VolBlocks = sdmr.VolBlocks;
248 mr.VolBytes = sdmr.VolBytes;
249 mr.VolMounts = sdmr.VolMounts;
250 mr.VolErrors = sdmr.VolErrors;
251 mr.VolWrites = sdmr.VolWrites;
252 mr.LastWritten = sdmr.LastWritten;
254 mr.InChanger = sdmr.InChanger;
255 mr.VolReadTime = sdmr.VolReadTime;
256 mr.VolWriteTime = sdmr.VolWriteTime;
257 mr.VolParts = sdmr.VolParts;
258 bstrncpy(mr.VolStatus, sdmr.VolStatus, sizeof(mr.VolStatus));
260 Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName);
262 * Check if it has expired, and if not update the DB. Note, if
263 * Volume has expired, has_volume_expired() will update the DB.
265 if (has_volume_expired(jcr, &mr) || db_update_media_record(jcr, jcr->db, &mr)) {
266 send_volume_info_to_storage_daemon(jcr, bs, &mr);
268 Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"),
269 db_strerror(jcr->db));
270 bnet_fsend(bs, _("1992 Update Media error\n"));
271 Dmsg0(400, "send error\n");
276 * Request to create a JobMedia record
278 } else if (sscanf(bs->msg, Create_job_media, &Job,
279 &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile,
280 &jm.StartBlock, &jm.EndBlock, &jm.Copy, &jm.Stripe) == 9) {
282 jm.JobId = jcr->JobId;
283 jm.MediaId = jcr->MediaId;
284 Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n",
285 jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex);
286 if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) {
287 Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"),
288 db_strerror(jcr->db));
289 bnet_fsend(bs, _("1991 Update JobMedia error\n"));
291 Dmsg0(400, "JobMedia record created\n");
292 bnet_fsend(bs, OK_create);
296 omsg = get_memory(bs->msglen+1);
297 pm_strcpy(omsg, bs->msg);
298 bnet_fsend(bs, _("1990 Invalid Catalog Request: %s"), omsg);
299 Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg);
302 Dmsg1(400, ">CatReq response: %s", bs->msg);
303 Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr);
308 * Update File Attributes in the catalog with data
309 * sent by the Storage daemon. Note, we receive the whole
310 * attribute record, but we select out only the stat packet,
311 * VolSessionId, VolSessionTime, FileIndex, and file name
312 * to store in the catalog.
314 void catalog_update(JCR *jcr, BSOCK *bs)
317 uint32_t VolSessionId, VolSessionTime;
327 if (!jcr->pool->catalog_files) {
328 return; /* user disabled cataloging */
331 omsg = get_memory(bs->msglen+1);
332 pm_strcpy(omsg, bs->msg);
333 bnet_fsend(bs, _("1991 Invalid Catalog Update: %s"), omsg);
334 Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog Update; DB not open: %s"), omsg);
339 /* Start transaction allocates jcr->attr and jcr->ar if needed */
340 db_start_transaction(jcr, jcr->db); /* start transaction if not already open */
343 /* Start by scanning directly in the message buffer to get Stream
344 * there may be a cached attr so we cannot yet write into
345 * jcr->attr or jcr->ar
348 skip_nonspaces(&p); /* UpdCat */
350 skip_nonspaces(&p); /* Job=nnn */
352 skip_nonspaces(&p); /* FileAttributes */
355 unser_uint32(VolSessionId);
356 unser_uint32(VolSessionTime);
357 unser_int32(FileIndex);
359 unser_uint32(data_len);
360 p += unser_length(p);
362 Dmsg1(400, "UpdCat msg=%s\n", bs->msg);
363 Dmsg5(400, "UpdCat VolSessId=%d VolSessT=%d FI=%d Strm=%d data_len=%d\n",
364 VolSessionId, VolSessionTime, FileIndex, Stream, data_len);
366 if (Stream == STREAM_UNIX_ATTRIBUTES || Stream == STREAM_UNIX_ATTRIBUTES_EX) {
367 if (jcr->cached_attribute) {
368 Dmsg2(400, "Cached attr. Stream=%d fname=%s\n", ar->Stream, ar->fname);
369 if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
370 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
373 /* Any cached attr is flushed so we can reuse jcr->attr and jcr->ar */
374 jcr->attr = check_pool_memory_size(jcr->attr, bs->msglen);
375 memcpy(jcr->attr, bs->msg, bs->msglen);
376 p = jcr->attr - bs->msg + p; /* point p into jcr->attr */
377 skip_nonspaces(&p); /* skip FileIndex */
379 skip_nonspaces(&p); /* skip FileType */
382 len = strlen(fname); /* length before attributes */
383 attr = &fname[len+1];
385 Dmsg2(400, "dird<stored: stream=%d %s\n", Stream, fname);
386 Dmsg1(400, "dird<stored: attr=%s\n", attr);
389 ar->FileIndex = FileIndex;
392 ar->JobId = jcr->JobId;
395 jcr->cached_attribute = true;
397 Dmsg2(400, "dird<filed: stream=%d %s\n", Stream, fname);
398 Dmsg1(400, "dird<filed: attr=%s\n", attr);
401 if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
402 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
405 } else if (Stream == STREAM_MD5_SIGNATURE || Stream == STREAM_SHA1_SIGNATURE) {
407 if (ar->FileIndex != FileIndex) {
408 Jmsg(jcr, M_WARNING, 0, _("Got MD5/SHA1 but not same File as attributes\n"));
410 /* Update signature in catalog */
411 char SIGbuf[50]; /* 24 bytes should be enough */
413 if (Stream == STREAM_MD5_SIGNATURE) {
420 bin_to_base64(SIGbuf, fname, len);
421 Dmsg3(400, "SIGlen=%d SIG=%s type=%d\n", strlen(SIGbuf), SIGbuf, Stream);
422 if (jcr->cached_attribute) {
425 Dmsg2(400, "Cached attr with SIG. Stream=%d fname=%s\n", ar->Stream, ar->fname);
426 if (!db_create_file_attributes_record(jcr, jcr->db, ar)) {
427 Jmsg1(jcr, M_FATAL, 0, _("Attribute create error. %s"), db_strerror(jcr->db));
429 jcr->cached_attribute = false;
431 if (!db_add_SIG_to_file_record(jcr, jcr->db, ar->FileId, SIGbuf, type)) {
432 Jmsg(jcr, M_ERROR, 0, _("Catalog error updating MD5/SHA1. %s"),
433 db_strerror(jcr->db));