+
+/*
+ * Check if any time limits or use limits have expired
+ * if so, set the VolStatus appropriately.
+ */
+bool has_volume_expired(JCR *jcr, MEDIA_DBR *mr)
+{
+ bool expired = false;
+ /*
+ * Check limits and expirations if "Append" and it has been used
+ * i.e. mr->VolJobs > 0
+ *
+ */
+ if (strcmp(mr->VolStatus, "Append") == 0 && mr->VolJobs > 0) {
+ /* First handle Max Volume Bytes */
+ if ((mr->MaxVolBytes > 0 && mr->VolBytes >= mr->MaxVolBytes)) {
+ Jmsg(jcr, M_INFO, 0, _("Max Volume bytes exceeded. "
+ "Marking Volume \"%s\" as Full.\n"), mr->VolumeName);
+ bstrncpy(mr->VolStatus, "Full", sizeof(mr->VolStatus));
+ expired = true;
+
+ /* Now see if Volume should only be used once */
+ } else if (mr->VolBytes > 0 && jcr->pool->use_volume_once) {
+ Jmsg(jcr, M_INFO, 0, _("Volume used once. "
+ "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
+ bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
+ expired = true;
+
+ /* Now see if Max Jobs written to volume */
+ } else if (mr->MaxVolJobs > 0 && mr->MaxVolJobs <= mr->VolJobs) {
+ Jmsg(jcr, M_INFO, 0, _("Max Volume jobs exceeded. "
+ "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
+ bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
+ expired = true;
+
+ /* Now see if Max Files written to volume */
+ } else if (mr->MaxVolFiles > 0 && mr->MaxVolFiles <= mr->VolFiles) {
+ Jmsg(jcr, M_INFO, 0, _("Max Volume files exceeded. "
+ "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
+ bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
+ expired = true;
+
+ /* Finally, check Use duration expiration */
+ } else if (mr->VolUseDuration > 0) {
+ utime_t now = time(NULL);
+ /* See if Vol Use has expired */
+ if (mr->VolUseDuration <= (now - mr->FirstWritten)) {
+ Jmsg(jcr, M_INFO, 0, _("Max configured use duration exceeded. "
+ "Marking Volume \"%s\" as Used.\n"), mr->VolumeName);
+ bstrncpy(mr->VolStatus, "Used", sizeof(mr->VolStatus));
+ expired = true;
+ }
+ }
+ }
+ if (expired) {
+ /* Need to update media */
+ if (!db_update_media_record(jcr, jcr->db, mr)) {
+ Jmsg(jcr, M_ERROR, 0, _("Catalog error updating volume \"%s\". ERR=%s"),
+ mr->VolumeName, db_strerror(jcr->db));
+ }
+ }
+ return expired;
+}
+
+/*
+ * Try hard to recycle the current volume
+ *
+ * Returns: on failure - reason = NULL
+ * on success - reason - pointer to reason
+ */
+void check_if_volume_valid_or_recyclable(JCR *jcr, MEDIA_DBR *mr, const char **reason)
+{
+ int ok;
+
+ *reason = NULL;
+
+ /* Check if a duration or limit has expired */
+ if (has_volume_expired(jcr, mr)) {
+ *reason = "volume has expired";
+ /* Keep going because we may be able to recycle volume */
+ }
+
+ /*
+ * Now see if we can use the volume as is
+ */
+ if (strcmp(mr->VolStatus, "Append") == 0 ||
+ strcmp(mr->VolStatus, "Recycle") == 0) {
+ *reason = NULL;
+ return;
+ }
+
+ /*
+ * Check if the Volume is already marked for recycling
+ */
+ if (strcmp(mr->VolStatus, "Purged") == 0) {
+ if (recycle_volume(jcr, mr)) {
+ Jmsg(jcr, M_INFO, 0, "Recycled current volume \"%s\"\n", mr->VolumeName);
+ *reason = NULL;
+ return;
+ } else {
+ /* In principle this shouldn't happen */
+ *reason = "and recycling of current volume failed";
+ return;
+ }
+ }
+
+ /* At this point, the volume is not valid for writing */
+ *reason = "but should be Append, Purged or Recycle";
+
+ /*
+ * What we're trying to do here is see if the current volume is
+ * "recyclable" - ie. if we prune all expired jobs off it, is
+ * it now possible to reuse it for the job that it is currently
+ * needed for?
+ */
+ if ((mr->LastWritten + mr->VolRetention) < (utime_t)time(NULL)
+ && mr->Recycle && jcr->pool->recycle_current_volume
+ && (strcmp(mr->VolStatus, "Full") == 0 ||
+ strcmp(mr->VolStatus, "Used") == 0)) {
+ /*
+ * Attempt prune of current volume to see if we can
+ * recycle it for use.
+ */
+ UAContext *ua;
+
+ ua = new_ua_context(jcr);
+ ok = prune_volume(ua, mr);
+ free_ua_context(ua);
+
+ if (ok) {
+ /* If fully purged, recycle current volume */
+ if (recycle_volume(jcr, mr)) {
+ Jmsg(jcr, M_INFO, 0, "Recycled current volume \"%s\"\n", mr->VolumeName);
+ *reason = NULL;
+ } else {
+ *reason = "but should be Append, Purged or Recycle (recycling of the "
+ "current volume failed)";
+ }
+ } else {
+ *reason = "but should be Append, Purged or Recycle (cannot automatically "
+ "recycle current volume, as it still contains unpruned data)";
+ }
+ }
+}