diff --git a/src/cats/protos.h b/src/cats/protos.h index 0e44276b5d4..793ba37cd97 100644 --- a/src/cats/protos.h +++ b/src/cats/protos.h @@ -81,7 +81,7 @@ bool db_delete_media_record(JCR *jcr, B_DB *mdb, MEDIA_DBR *mr); bool db_find_last_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime, char *job, int JobLevel); bool db_find_job_start_time(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM **stime, char *job); bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr); -int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr); +int db_find_next_volume(JCR *jcr, B_DB *mdb, int index, bool InChanger, MEDIA_DBR *mr, const char *unwanted_volumes); bool db_find_failed_job_since(JCR *jcr, B_DB *mdb, JOB_DBR *jr, POOLMEM *stime, int &JobLevel); /* sql_get.c */ diff --git a/src/cats/sql_find.c b/src/cats/sql_find.c index 0f6a75a7b6f..9a89787f3a3 100644 --- a/src/cats/sql_find.c +++ b/src/cats/sql_find.c @@ -307,70 +307,115 @@ bool db_find_last_jobid(JCR *jcr, B_DB *mdb, const char *Name, JOB_DBR *jr) return retval; } +/* + * Search a comma separated list of unwanted volume names and see if given VolumeName is on it. + */ +static inline bool is_on_unwanted_volumes_list(const char *VolumeName, + const char *unwanted_volumes) +{ + bool retval = false; + char *list_item, *bp, *unwanted_volumes_list; + + unwanted_volumes_list = bstrdup(unwanted_volumes); + list_item = unwanted_volumes_list; + while (list_item) { + bp = strchr(list_item, ','); + if (bp) { + *bp++ = '\0'; + } + + if (bstrcmp(VolumeName, list_item)) { + retval = true; + goto bail_out; + } + list_item = bp; + } + +bail_out: + free(unwanted_volumes_list); + return retval; +} + /* * Find Available Media (Volume) for Pool * * Find a Volume for a given PoolId, MediaType, and Status. + * The unwanted_volumes variable lists the VolumeNames which we should skip if any. * * Returns: 0 on failure * numrows on success */ -int db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr) +int db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR *mr, const char *unwanted_volumes) { - SQL_ROW row = NULL; + char ed1[50]; int num_rows = 0; - const char *order; + SQL_ROW row = NULL; + bool find_oldest = false; + bool found_candidate = false; char esc_type[MAX_ESCAPE_NAME_LENGTH]; char esc_status[MAX_ESCAPE_NAME_LENGTH]; - char ed1[50]; db_lock(mdb); + mdb->db_escape_string(jcr, esc_type, mr->MediaType, strlen(mr->MediaType)); mdb->db_escape_string(jcr, esc_status, mr->VolStatus, strlen(mr->VolStatus)); - if (item == -1) { /* find oldest volume */ - /* Find oldest volume */ + if (item == -1) { + find_oldest = true; + item = 1; + } + +retry_fetch: + if (find_oldest) { + /* + * Find oldest volume(s) + */ Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks," - "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes," - "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs," - "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger," - "EndFile,EndBlock,LabelType,LabelDate,StorageId," - "Enabled,LocationId,RecycleCount,InitialWrite," - "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime," - "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize " - "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full'," - "'Recycle','Purged','Used','Append') AND Enabled=1 " - "ORDER BY LastWritten LIMIT 1", - edit_int64(mr->PoolId, ed1), esc_type); - item = 1; + "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes," + "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs," + "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger," + "EndFile,EndBlock,LabelType,LabelDate,StorageId," + "Enabled,LocationId,RecycleCount,InitialWrite," + "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime," + "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize " + "FROM Media WHERE PoolId=%s AND MediaType='%s' AND VolStatus IN ('Full'," + "'Recycle','Purged','Used','Append') AND Enabled=1 " + "ORDER BY LastWritten LIMIT %d", + edit_int64(mr->PoolId, ed1), esc_type, item); } else { POOL_MEM changer(PM_FNAME); - /* Find next available volume */ + const char *order; + + /* + * Find next available volume + */ if (InChanger) { - Mmsg(changer, "AND InChanger=1 AND StorageId=%s", - edit_int64(mr->StorageId, ed1)); + Mmsg(changer, "AND InChanger=1 AND StorageId=%s", edit_int64(mr->StorageId, ed1)); } + if (bstrcmp(mr->VolStatus, "Recycle") || bstrcmp(mr->VolStatus, "Purged")) { order = "AND Recycle=1 ORDER BY LastWritten ASC,MediaId"; /* take oldest that can be recycled */ } else { order = sql_media_order_most_recently_written[db_get_type_index(mdb)]; /* take most recently written */ } + Mmsg(mdb->cmd, "SELECT MediaId,VolumeName,VolJobs,VolFiles,VolBlocks," - "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes," - "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs," - "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger," - "EndFile,EndBlock,LabelType,LabelDate,StorageId," - "Enabled,LocationId,RecycleCount,InitialWrite," - "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime," - "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize " - "FROM Media WHERE PoolId=%s AND MediaType='%s' AND Enabled=1 " - "AND VolStatus='%s' " - "%s " - "%s LIMIT %d", - edit_int64(mr->PoolId, ed1), esc_type, - esc_status, changer.c_str(), order, item); + "VolBytes,VolMounts,VolErrors,VolWrites,MaxVolBytes,VolCapacityBytes," + "MediaType,VolStatus,PoolId,VolRetention,VolUseDuration,MaxVolJobs," + "MaxVolFiles,Recycle,Slot,FirstWritten,LastWritten,InChanger," + "EndFile,EndBlock,LabelType,LabelDate,StorageId," + "Enabled,LocationId,RecycleCount,InitialWrite," + "ScratchPoolId,RecyclePoolId,VolReadTime,VolWriteTime," + "ActionOnPurge,EncryptionKey,MinBlocksize,MaxBlocksize " + "FROM Media WHERE PoolId=%s AND MediaType='%s' AND Enabled=1 " + "AND VolStatus='%s' " + "%s " + "%s LIMIT %d", + edit_int64(mr->PoolId, ed1), esc_type, + esc_status, changer.c_str(), order, item); } + Dmsg1(100, "fnextvol=%s\n", mdb->cmd); if (!QUERY_DB(jcr, mdb, mdb->cmd)) { goto bail_out; @@ -379,78 +424,89 @@ int db_find_next_volume(JCR *jcr, B_DB *mdb, int item, bool InChanger, MEDIA_DBR num_rows = sql_num_rows(mdb); if (item > num_rows || item < 1) { Dmsg2(050, "item=%d got=%d\n", item, num_rows); - Mmsg2(&mdb->errmsg, _("Request for Volume item %d greater than max %d or less than 1\n"), - item, num_rows); + Mmsg2(&mdb->errmsg, _("Request for Volume item %d greater than max %d or less than 1\n"), item, num_rows); num_rows = 0; goto bail_out; } - /* Note, we previously seeked to the row using: sql_data_seek(mdb, item-1); - * but this failed on PostgreSQL, so now we loop over all the records. - * This should not be too horrible since the maximum Volumes we look at - * in any case is 20. - */ - while (item-- > 0) { + for (int i = 0 ; i < item; i++) { if ((row = sql_fetch_row(mdb)) == NULL) { - Dmsg1(050, "Fail fetch item=%d\n", item+1); - Mmsg1(&mdb->errmsg, _("No Volume record found for item %d.\n"), item); + Dmsg1(050, "Fail fetch item=%d\n", i); + Mmsg1(&mdb->errmsg, _("No Volume record found for item %d.\n"), i); sql_free_result(mdb); num_rows = 0; goto bail_out; } - } - /* Return fields in Media Record */ - mr->MediaId = str_to_int64(row[0]); - bstrncpy(mr->VolumeName, (row[1] != NULL) ? row[1] : "", sizeof(mr->VolumeName)); - mr->VolJobs = str_to_int64(row[2]); - mr->VolFiles = str_to_int64(row[3]); - mr->VolBlocks = str_to_int64(row[4]); - mr->VolBytes = str_to_uint64(row[5]); - mr->VolMounts = str_to_int64(row[6]); - mr->VolErrors = str_to_int64(row[7]); - mr->VolWrites = str_to_int64(row[8]); - mr->MaxVolBytes = str_to_uint64(row[9]); - mr->VolCapacityBytes = str_to_uint64(row[10]); - bstrncpy(mr->MediaType, (row[11] != NULL) ? row[11] : "", sizeof(mr->MediaType)); - bstrncpy(mr->VolStatus, (row[12] != NULL) ? row[12] : "", sizeof(mr->VolStatus)); - mr->PoolId = str_to_int64(row[13]); - mr->VolRetention = str_to_uint64(row[14]); - mr->VolUseDuration = str_to_uint64(row[15]); - mr->MaxVolJobs = str_to_int64(row[16]); - mr->MaxVolFiles = str_to_int64(row[17]); - mr->Recycle = str_to_int64(row[18]); - mr->Slot = str_to_int64(row[19]); - bstrncpy(mr->cFirstWritten, (row[20] != NULL) ? row[20] : "", sizeof(mr->cFirstWritten)); - mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten); - bstrncpy(mr->cLastWritten, (row[21] != NULL) ? row[21] : "", sizeof(mr->cLastWritten)); - mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten); - mr->InChanger = str_to_uint64(row[22]); - mr->EndFile = str_to_uint64(row[23]); - mr->EndBlock = str_to_uint64(row[24]); - mr->LabelType = str_to_int64(row[25]); - bstrncpy(mr->cLabelDate, (row[26] != NULL) ? row[26] : "", sizeof(mr->cLabelDate)); - mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate); - mr->StorageId = str_to_int64(row[27]); - mr->Enabled = str_to_int64(row[28]); - mr->LocationId = str_to_int64(row[29]); - mr->RecycleCount = str_to_int64(row[30]); - bstrncpy(mr->cInitialWrite, (row[31] != NULL) ? row[31] : "", sizeof(mr->cInitialWrite)); - mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite); - mr->ScratchPoolId = str_to_int64(row[32]); - mr->RecyclePoolId = str_to_int64(row[33]); - mr->VolReadTime = str_to_int64(row[34]); - mr->VolWriteTime = str_to_int64(row[35]); - mr->ActionOnPurge = str_to_int64(row[36]); - bstrncpy(mr->EncrKey, (row[37] != NULL) ? row[37] : "", sizeof(mr->EncrKey)); - mr->MinBlocksize = str_to_int32(row[38]); - mr->MaxBlocksize = str_to_int32(row[39]); + /* + * See if this is not on the unwanted volumes list. + */ + if (unwanted_volumes && is_on_unwanted_volumes_list(row[1], unwanted_volumes)) { + continue; + } - sql_free_result(mdb); + /* + * Return fields in Media Record + */ + mr->MediaId = str_to_int64(row[0]); + bstrncpy(mr->VolumeName, (row[1] != NULL) ? row[1] : "", sizeof(mr->VolumeName)); + mr->VolJobs = str_to_int64(row[2]); + mr->VolFiles = str_to_int64(row[3]); + mr->VolBlocks = str_to_int64(row[4]); + mr->VolBytes = str_to_uint64(row[5]); + mr->VolMounts = str_to_int64(row[6]); + mr->VolErrors = str_to_int64(row[7]); + mr->VolWrites = str_to_int64(row[8]); + mr->MaxVolBytes = str_to_uint64(row[9]); + mr->VolCapacityBytes = str_to_uint64(row[10]); + bstrncpy(mr->MediaType, (row[11] != NULL) ? row[11] : "", sizeof(mr->MediaType)); + bstrncpy(mr->VolStatus, (row[12] != NULL) ? row[12] : "", sizeof(mr->VolStatus)); + mr->PoolId = str_to_int64(row[13]); + mr->VolRetention = str_to_uint64(row[14]); + mr->VolUseDuration = str_to_uint64(row[15]); + mr->MaxVolJobs = str_to_int64(row[16]); + mr->MaxVolFiles = str_to_int64(row[17]); + mr->Recycle = str_to_int64(row[18]); + mr->Slot = str_to_int64(row[19]); + bstrncpy(mr->cFirstWritten, (row[20] != NULL) ? row[20] : "", sizeof(mr->cFirstWritten)); + mr->FirstWritten = (time_t)str_to_utime(mr->cFirstWritten); + bstrncpy(mr->cLastWritten, (row[21] != NULL) ? row[21] : "", sizeof(mr->cLastWritten)); + mr->LastWritten = (time_t)str_to_utime(mr->cLastWritten); + mr->InChanger = str_to_uint64(row[22]); + mr->EndFile = str_to_uint64(row[23]); + mr->EndBlock = str_to_uint64(row[24]); + mr->LabelType = str_to_int64(row[25]); + bstrncpy(mr->cLabelDate, (row[26] != NULL) ? row[26] : "", sizeof(mr->cLabelDate)); + mr->LabelDate = (time_t)str_to_utime(mr->cLabelDate); + mr->StorageId = str_to_int64(row[27]); + mr->Enabled = str_to_int64(row[28]); + mr->LocationId = str_to_int64(row[29]); + mr->RecycleCount = str_to_int64(row[30]); + bstrncpy(mr->cInitialWrite, (row[31] != NULL) ? row[31] : "", sizeof(mr->cInitialWrite)); + mr->InitialWrite = (time_t)str_to_utime(mr->cInitialWrite); + mr->ScratchPoolId = str_to_int64(row[32]); + mr->RecyclePoolId = str_to_int64(row[33]); + mr->VolReadTime = str_to_int64(row[34]); + mr->VolWriteTime = str_to_int64(row[35]); + mr->ActionOnPurge = str_to_int64(row[36]); + bstrncpy(mr->EncrKey, (row[37] != NULL) ? row[37] : "", sizeof(mr->EncrKey)); + mr->MinBlocksize = str_to_int32(row[38]); + mr->MaxBlocksize = str_to_int32(row[39]); + + sql_free_result(mdb); + found_candidate = true; + break; + } + + if (!found_candidate && find_oldest) { + item++; + goto retry_fetch; + } bail_out: db_unlock(mdb); Dmsg1(050, "Rtn numrows=%d\n", num_rows); + return num_rows; } diff --git a/src/dird/catreq.c b/src/dird/catreq.c index e9bcc2ec0f8..2e53136fdc0 100644 --- a/src/dird/catreq.c +++ b/src/dird/catreq.c @@ -45,7 +45,7 @@ * Requests from the Storage daemon */ static char Find_media[] = - "CatReq Job=%127s FindMedia=%d pool_name=%127s media_type=%127s\n"; + "CatReq Job=%127s FindMedia=%d pool_name=%127s media_type=%127s unwanted_volumes=%s\n"; static char Get_Vol_Info[] = "CatReq Job=%127s GetVolInfo VolName=%127s write=%d\n"; static char Update_media[] = @@ -104,6 +104,7 @@ void catalog_request(JCR *jcr, BSOCK *bs) JOBMEDIA_DBR jm; char Job[MAX_NAME_LENGTH]; char pool_name[MAX_NAME_LENGTH]; + POOL_MEM unwanted_volumes(PM_MESSAGE); int index, ok, label, writing; POOLMEM *omsg; POOL_DBR pr; @@ -122,17 +123,20 @@ void catalog_request(JCR *jcr, BSOCK *bs) */ Dmsg1(100, "catreq %s", bs->msg); if (!jcr->db) { - omsg = get_memory(bs->msglen+1); + omsg = get_memory(bs->msglen + 1); pm_strcpy(omsg, bs->msg); bs->fsend(_("1990 Invalid Catalog Request: %s"), omsg); Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request; DB not open: %s"), omsg); free_memory(omsg); + return; } + /* * Find next appendable medium for SD */ - if (sscanf(bs->msg, Find_media, &Job, &index, &pool_name, &mr.MediaType) == 4) { + unwanted_volumes.check_size(bs->msglen); + if (sscanf(bs->msg, Find_media, &Job, &index, &pool_name, &mr.MediaType, unwanted_volumes.c_str()) == 5) { memset(&pr, 0, sizeof(pr)); bstrncpy(pr.Name, pool_name, sizeof(pr.Name)); unbash_spaces(pr.Name); @@ -141,9 +145,10 @@ void catalog_request(JCR *jcr, BSOCK *bs) mr.PoolId = pr.PoolId; set_storageid_in_mr(jcr->res.wstore, &mr); mr.ScratchPoolId = pr.ScratchPoolId; - ok = find_next_volume_for_append(jcr, &mr, index, fnv_create_vol, fnv_prune); + ok = find_next_volume_for_append(jcr, &mr, index, unwanted_volumes.c_str(), fnv_create_vol, fnv_prune); Dmsg3(050, "find_media ok=%d idx=%d vol=%s\n", ok, index, mr.VolumeName); } + /* * Send Find Media response to Storage daemon */ @@ -153,12 +158,12 @@ void catalog_request(JCR *jcr, BSOCK *bs) bs->fsend(_("1901 No Media.\n")); Dmsg0(500, "1901 No Media.\n"); } - - /* - * Request to find specific Volume information - */ } else if (sscanf(bs->msg, Get_Vol_Info, &Job, &mr.VolumeName, &writing) == 3) { + /* + * Request to find specific Volume information + */ Dmsg1(100, "CatReq GetVolInfo Vol=%s\n", mr.VolumeName); + /* * Find the Volume */ @@ -189,9 +194,11 @@ void catalog_request(JCR *jcr, BSOCK *bs) check_if_volume_valid_or_recyclable(jcr, &mr, &reason); } } + if (!reason && mr.Enabled != 1) { reason = _("is not Enabled"); } + if (reason == NULL) { /* * Send Find Media response to Storage daemon @@ -202,36 +209,34 @@ void catalog_request(JCR *jcr, BSOCK *bs) bs->fsend(_("1998 Volume \"%s\" catalog status is %s, %s.\n"), mr.VolumeName, mr.VolStatus, reason); } - } else { bs->fsend(_("1997 Volume \"%s\" not in catalog.\n"), mr.VolumeName); Dmsg1(100, "1997 Volume \"%s\" not in catalog.\n", mr.VolumeName); } - - /* - * Request to update Media record. Comes typically at the end - * of a Storage daemon Job Session, when labeling/relabeling a - * Volume, or when an EOF mark is written. - */ } else if (sscanf(bs->msg, Update_media, &Job, &sdmr.VolumeName, &sdmr.VolJobs, &sdmr.VolFiles, &sdmr.VolBlocks, &sdmr.VolBytes, &sdmr.VolMounts, &sdmr.VolErrors, &sdmr.VolWrites, &sdmr.MaxVolBytes, &VolLastWritten, &sdmr.VolStatus, &sdmr.Slot, &label, &sdmr.InChanger, &sdmr.VolReadTime, &sdmr.VolWriteTime, &VolFirstWritten) == 18) { + /* + * Request to update Media record. Comes typically at the end + * of a Storage daemon Job Session, when labeling/relabeling a + * Volume, or when an EOF mark is written. + */ db_lock(jcr->db); - Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName, - mr.VolStatus, sdmr.VolStatus); + Dmsg3(400, "Update media %s oldStat=%s newStat=%s\n", sdmr.VolumeName, mr.VolStatus, sdmr.VolStatus); bstrncpy(mr.VolumeName, sdmr.VolumeName, sizeof(mr.VolumeName)); /* copy Volume name */ unbash_spaces(mr.VolumeName); if (!db_get_media_record(jcr, jcr->db, &mr)) { - Jmsg(jcr, M_ERROR, 0, _("Unable to get Media record for Volume %s: ERR=%s\n"), - mr.VolumeName, db_strerror(jcr->db)); - bs->fsend(_("1991 Catalog Request for vol=%s failed: %s"), - mr.VolumeName, db_strerror(jcr->db)); + Jmsg(jcr, M_ERROR, 0, _("Unable to get Media record for Volume %s: ERR=%s\n"), mr.VolumeName, db_strerror(jcr->db)); + bs->fsend(_("1991 Catalog Request for vol=%s failed: %s"), mr.VolumeName, db_strerror(jcr->db)); goto bail_out; } - /* Set first written time if this is first job */ + + /* + * Set first written time if this is first job + */ if (mr.FirstWritten == 0) { if (VolFirstWritten == 0) { mr.FirstWritten = jcr->start_time; /* use Job start time as first write */ @@ -240,7 +245,10 @@ void catalog_request(JCR *jcr, BSOCK *bs) } mr.set_first_written = true; } - /* If we just labeled the tape set time */ + + /* + * If we just labeled the tape set time + */ if (label || mr.LabelDate == 0) { mr.LabelDate = jcr->start_time; mr.set_label_date = true; @@ -274,20 +282,25 @@ void catalog_request(JCR *jcr, BSOCK *bs) /* * Update to point to the last device used to write the Volume. - * However, do so only if we are writing the tape, i.e. - * the number of VolWrites has increased. + * However, do so only if we are writing the tape, i.e. + * the number of VolWrites has increased. */ if (jcr->res.wstore && sdmr.VolWrites > mr.VolWrites) { - Dmsg2(050, "Update StorageId old=%d new=%d\n", - mr.StorageId, jcr->res.wstore->StorageId); - /* Update StorageId after write */ + Dmsg2(050, "Update StorageId old=%d new=%d\n", mr.StorageId, jcr->res.wstore->StorageId); + /* + * Update StorageId after write + */ set_storageid_in_mr(jcr->res.wstore, &mr); } else { - /* Nothing written, reset same StorageId */ + /* + * Nothing written, reset same StorageId + */ set_storageid_in_mr(NULL, &mr); } - /* Copy updated values to original media record */ + /* + * Copy updated values to original media record + */ mr.VolJobs = sdmr.VolJobs; mr.VolFiles = sdmr.VolFiles; mr.VolBlocks = sdmr.VolBlocks; @@ -302,9 +315,10 @@ void catalog_request(JCR *jcr, BSOCK *bs) mr.VolWriteTime = sdmr.VolWriteTime; Dmsg2(400, "db_update_media_record. Stat=%s Vol=%s\n", mr.VolStatus, mr.VolumeName); + /* - * Update the database, then before sending the response to the - * SD, check if the Volume has expired. + * Update the database, then before sending the response to the SD, + * check if the Volume has expired. */ if (!db_update_media_record(jcr, jcr->db, &mr)) { Jmsg(jcr, M_FATAL, 0, _("Catalog error updating Media record. %s"), @@ -322,13 +336,12 @@ void catalog_request(JCR *jcr, BSOCK *bs) Dmsg1(400, ">CatReq response: %s", bs->msg); Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr); return; - /* - * Request to create a JobMedia record - */ } else if (sscanf(bs->msg, Create_job_media, &Job, - &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile, - &jm.StartBlock, &jm.EndBlock, &Copy, &Stripe, &MediaId) == 10) { - + &jm.FirstIndex, &jm.LastIndex, &jm.StartFile, &jm.EndFile, + &jm.StartBlock, &jm.EndBlock, &Copy, &Stripe, &MediaId) == 10) { + /* + * Request to create a JobMedia record + */ if (jcr->mig_jcr) { jm.JobId = jcr->mig_jcr->JobId; } else { @@ -336,18 +349,16 @@ void catalog_request(JCR *jcr, BSOCK *bs) } jm.MediaId = MediaId; Dmsg6(400, "create_jobmedia JobId=%d MediaId=%d SF=%d EF=%d FI=%d LI=%d\n", - jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex); + jm.JobId, jm.MediaId, jm.StartFile, jm.EndFile, jm.FirstIndex, jm.LastIndex); if (!db_create_jobmedia_record(jcr, jcr->db, &jm)) { - Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"), - db_strerror(jcr->db)); + Jmsg(jcr, M_FATAL, 0, _("Catalog error creating JobMedia record. %s"), db_strerror(jcr->db)); bs->fsend(_("1992 Create JobMedia error\n")); } else { Dmsg0(400, "JobMedia record created\n"); bs->fsend(OK_create); } - } else { - omsg = get_memory(bs->msglen+1); + omsg = get_memory(bs->msglen + 1); pm_strcpy(omsg, bs->msg); bs->fsend(_("1990 Invalid Catalog Request: %s"), omsg); Jmsg1(jcr, M_FATAL, 0, _("Invalid Catalog request: %s"), omsg); @@ -356,6 +367,7 @@ void catalog_request(JCR *jcr, BSOCK *bs) Dmsg1(400, ">CatReq response: %s", bs->msg); Dmsg1(400, "Leave catreq jcr 0x%x\n", jcr); + return; } diff --git a/src/dird/dird.h b/src/dird/dird.h index 7d21ddafdba..dbfd9970c49 100644 --- a/src/dird/dird.h +++ b/src/dird/dird.h @@ -65,10 +65,10 @@ struct del_ctx { /* Flags for find_next_volume_for_append() */ enum { - fnv_create_vol = true, + fnv_create_vol = true, fnv_no_create_vol = false, - fnv_prune = true, - fnv_no_prune = false + fnv_prune = true, + fnv_no_prune = false }; enum e_prtmsg { diff --git a/src/dird/next_vol.c b/src/dird/next_vol.c index 496370af720..9e55bab3397 100644 --- a/src/dird/next_vol.c +++ b/src/dird/next_vol.c @@ -52,10 +52,12 @@ void set_storageid_in_mr(STORERES *store, MEDIA_DBR *mr) * jcr->db * jcr->pool * MEDIA_DBR mr with PoolId set + * unwanted_volumes -- list of volumes we don't want * create -- whether or not to create a new volume * prune -- whether or not to prune volumes */ -int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create, bool prune) +int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, + const char *unwanted_volumes, bool create, bool prune) { int retry = 0; bool ok; @@ -76,14 +78,13 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create, */ db_lock(jcr->db); while (1) { - bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); /* want only appendable volumes */ - /* * 1. Look for volume with "Append" status. */ set_storageid_in_mr(store, mr); - ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr); + bstrncpy(mr->VolStatus, "Append", sizeof(mr->VolStatus)); + ok = db_find_next_volume(jcr, jcr->db, index, InChanger, mr, unwanted_volumes); if (!ok) { /* * No volume found, apply algorithm @@ -94,14 +95,14 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create, /* * 2. Try finding a recycled volume */ - ok = find_recycled_volume(jcr, InChanger, mr, store); + ok = find_recycled_volume(jcr, InChanger, mr, store, unwanted_volumes); set_storageid_in_mr(store, mr); Dmsg2(dbglvl, "find_recycled_volume ok=%d FW=%d\n", ok, mr->FirstWritten); if (!ok) { /* * 3. Try recycling any purged volume */ - ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store); + ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store, unwanted_volumes); set_storageid_in_mr(store, mr); if (!ok) { /* @@ -111,7 +112,7 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create, Dmsg0(dbglvl, "Call prune_volumes\n"); prune_volumes(jcr, InChanger, mr, store); } - ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store); + ok = recycle_oldest_purged_volume(jcr, InChanger, mr, store, unwanted_volumes); set_storageid_in_mr(store, mr); /* put StorageId in new record */ if (!ok && create) { Dmsg4(dbglvl, "after prune volumes_vol ok=%d index=%d InChanger=%d Vstat=%s\n", @@ -154,7 +155,7 @@ int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, bool create, * Find oldest volume to recycle */ set_storageid_in_mr(store, mr); - ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr); + ok = db_find_next_volume(jcr, jcr->db, -1, InChanger, mr, unwanted_volumes); set_storageid_in_mr(store, mr); Dmsg1(dbglvl, "Find oldest=%d Volume\n", ok); if (ok && prune) { @@ -421,11 +422,11 @@ bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr, STORERES *store * then try to take the oldest volume. */ set_storageid_in_mr(store, &smr); /* put StorageId in new record */ - if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr)) { + if (db_find_next_volume(jcr, jcr->db, 1, InChanger, &smr, NULL)) { found = true; - } else if (find_recycled_volume(jcr, InChanger, &smr, store)) { + } else if (find_recycled_volume(jcr, InChanger, &smr, store, NULL)) { found = true; - } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr, store)) { + } else if (recycle_oldest_purged_volume(jcr, InChanger, &smr, store, NULL)) { found = true; } diff --git a/src/dird/protos.h b/src/dird/protos.h index 23b771a0733..628cfd394fe 100644 --- a/src/dird/protos.h +++ b/src/dird/protos.h @@ -41,11 +41,11 @@ void prune_volumes(JCR *jcr, bool InChanger, MEDIA_DBR *mr, STORERES *store); /* autorecycle.c */ -bool recycle_oldest_purged_volume(JCR *jcr, bool InChanger, - MEDIA_DBR *mr, STORERES *store); -int recycle_volume(JCR *jcr, MEDIA_DBR *mr); -bool find_recycled_volume(JCR *jcr, bool InChanger, - MEDIA_DBR *mr, STORERES *store); +bool find_recycled_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr, + STORERES *store, const char *unwanted_volumes); +bool recycle_oldest_purged_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr, + STORERES *store, const char *unwanted_volumes); +bool recycle_volume(JCR *jcr, MEDIA_DBR *mr); /* backup.c */ int wait_for_job_termination(JCR *jcr, int timeout = 0); @@ -189,7 +189,7 @@ void do_ndmp_client_status(UAContext *ua, CLIENTRES *client, char *cmd); /* next_vol.c */ void set_storageid_in_mr(STORERES *store, MEDIA_DBR *mr); int find_next_volume_for_append(JCR *jcr, MEDIA_DBR *mr, int index, - bool create, bool purge); + const char *unwanted_volumes, bool create, bool purge); bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr); void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason); bool get_scratch_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr, diff --git a/src/dird/recycle.c b/src/dird/recycle.c index ff5275d17b4..3f8e1121825 100644 --- a/src/dird/recycle.c +++ b/src/dird/recycle.c @@ -30,11 +30,12 @@ /* Forward referenced functions */ -bool find_recycled_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr, STORERES *store) +bool find_recycled_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr, + STORERES *store, const char *unwanted_volumes) { bstrncpy(mr->VolStatus, "Recycle", sizeof(mr->VolStatus)); set_storageid_in_mr(store, mr); - if (db_find_next_volume(jcr, jcr->db, 1, InChanger, mr)) { + if (db_find_next_volume(jcr, jcr->db, 1, InChanger, mr, unwanted_volumes)) { jcr->MediaId = mr->MediaId; Dmsg1(20, "Find_next_vol MediaId=%u\n", jcr->MediaId); pm_strcpy(jcr->VolumeName, mr->VolumeName); @@ -49,13 +50,13 @@ bool find_recycled_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr, STORERES *sto /* * Look for oldest Purged volume */ -bool recycle_oldest_purged_volume(JCR *jcr, bool InChanger, - MEDIA_DBR *mr, STORERES *store) +bool recycle_oldest_purged_volume(JCR *jcr, bool InChanger, MEDIA_DBR *mr, + STORERES *store, const char *unwanted_volumes) { bstrncpy(mr->VolStatus, "Purged", sizeof(mr->VolStatus)); set_storageid_in_mr(store, mr); - if (db_find_next_volume(jcr, jcr->db, 1, InChanger, mr)) { + if (db_find_next_volume(jcr, jcr->db, 1, InChanger, mr, unwanted_volumes)) { Dmsg1(20, "Find_next_vol MediaId=%u\n", mr->MediaId); set_storageid_in_mr(store, mr); if (recycle_volume(jcr, mr)) { @@ -73,7 +74,7 @@ bool recycle_oldest_purged_volume(JCR *jcr, bool InChanger, /* * Recycle the specified volume */ -int recycle_volume(JCR *jcr, MEDIA_DBR *mr) +bool recycle_volume(JCR *jcr, MEDIA_DBR *mr) { bstrncpy(mr->VolStatus, "Recycle", sizeof(mr->VolStatus)); mr->VolJobs = mr->VolFiles = mr->VolBlocks = mr->VolErrors = 0; diff --git a/src/dird/ua_output.c b/src/dird/ua_output.c index a97a3a29525..cb9dd4477e7 100644 --- a/src/dird/ua_output.c +++ b/src/dird/ua_output.c @@ -674,7 +674,7 @@ static bool list_nextvol(UAContext *ua, int ndays) get_job_storage(&store, job, run); set_storageid_in_mr(store.store, &mr); /* no need to set ScratchPoolId, since we use fnv_no_create_vol */ - if (!find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_prune)) { + if (!find_next_volume_for_append(jcr, &mr, 1, NULL, fnv_no_create_vol, fnv_prune)) { ua->error_msg(_("Could not find next Volume for Job %s (Pool=%s, Level=%s).\n"), job->name(), pr.Name, level_to_str(run->level)); } else { diff --git a/src/dird/ua_status.c b/src/dird/ua_status.c index 4d998f41ce4..808a3c04623 100644 --- a/src/dird/ua_status.c +++ b/src/dird/ua_status.c @@ -844,7 +844,7 @@ static void prt_runtime(UAContext *ua, sched_pkt *sp) set_storageid_in_mr(jcr->res.wstore, &mr); Dmsg0(250, "call find_next_volume_for_append\n"); /* no need to set ScratchPoolId, since we use fnv_no_create_vol */ - ok = find_next_volume_for_append(jcr, &mr, 1, fnv_no_create_vol, fnv_no_prune); + ok = find_next_volume_for_append(jcr, &mr, 1, NULL, fnv_no_create_vol, fnv_no_prune); } if (!ok) { bstrncpy(mr.VolumeName, "*unknown*", sizeof(mr.VolumeName)); diff --git a/src/stored/askdir.c b/src/stored/askdir.c index 55225412c72..e1737a23934 100644 --- a/src/stored/askdir.c +++ b/src/stored/askdir.c @@ -36,7 +36,7 @@ static pthread_mutex_t vol_info_mutex = PTHREAD_MUTEX_INITIALIZER; /* Requests sent to the Director */ static char Find_media[] = - "CatReq Job=%s FindMedia=%d pool_name=%s media_type=%s\n"; + "CatReq Job=%s FindMedia=%d pool_name=%s media_type=%s unwanted_volumes=%s\n"; static char Get_Vol_Info[] = "CatReq Job=%s GetVolInfo VolName=%s write=%d\n"; static char Update_media[] = @@ -266,37 +266,40 @@ bool SD_DCR::dir_get_volume_info(enum get_vol_info_rw writing) */ bool SD_DCR::dir_find_next_appendable_volume() { + bool retval; BSOCK *dir = jcr->dir_bsock; - bool rtn; - char lastVolume[MAX_NAME_LENGTH]; + POOL_MEM unwanted_volumes(PM_MESSAGE); Dmsg2(dbglvl, "dir_find_next_appendable_volume: reserved=%d Vol=%s\n", is_reserved(), VolumeName); /* - * Try the twenty oldest or most available volumes. Note, + * Try the twenty oldest or most available volumes. Note, * the most available could already be mounted on another * drive, so we continue looking for a not in use Volume. */ lock_volumes(); P(vol_info_mutex); clear_found_in_use(); - lastVolume[0] = 0; + + pm_strcpy(unwanted_volumes, ""); for (int vol_index = 1; vol_index < 20; vol_index++) { bash_spaces(media_type); bash_spaces(pool_name); - dir->fsend(Find_media, jcr->Job, vol_index, pool_name, media_type); + bash_spaces(unwanted_volumes.c_str()); + dir->fsend(Find_media, jcr->Job, vol_index, pool_name, media_type, unwanted_volumes.c_str()); unbash_spaces(media_type); unbash_spaces(pool_name); + unbash_spaces(unwanted_volumes.c_str()); Dmsg1(dbglvl, ">dird %s", dir->msg); + if (do_get_volume_info(this)) { - /* - * Give up if we get the same volume name twice - */ - if (lastVolume[0] && bstrcmp(lastVolume, VolumeName)) { - Dmsg1(dbglvl, "Got same vol = %s\n", lastVolume); - break; + if (vol_index == 1) { + pm_strcpy(unwanted_volumes, VolumeName); + } else { + pm_strcat(unwanted_volumes, ","); + pm_strcat(unwanted_volumes, VolumeName); } - bstrncpy(lastVolume, VolumeName, sizeof(lastVolume)); + if (can_i_write_volume()) { Dmsg1(dbglvl, "Call reserve_volume for write. Vol=%s\n", VolumeName); if (reserve_volume(this, VolumeName) == NULL) { @@ -304,7 +307,7 @@ bool SD_DCR::dir_find_next_appendable_volume() continue; } Dmsg1(dbglvl, "dir_find_next_appendable_volume return true. vol=%s\n", VolumeName); - rtn = true; + retval = true; goto get_out; } else { Dmsg1(dbglvl, "Volume %s is in use.\n", VolumeName); @@ -319,14 +322,14 @@ bool SD_DCR::dir_find_next_appendable_volume() Dmsg2(dbglvl, "No vol. index %d return false. dev=%s\n", vol_index, dev->print_name()); break; } - rtn = false; + retval = false; VolumeName[0] = 0; get_out: V(vol_info_mutex); unlock_volumes(); - return rtn; + return retval; } /**