]> git.ipfire.org Git - thirdparty/tvheadend.git/commitdiff
dvr: properly abort recordings when running out of free disk space
authorGlenn-1990 <g_christiaensen@msn.com>
Sun, 22 Jan 2017 15:59:16 +0000 (16:59 +0100)
committerJaroslav Kysela <perex@perex.cz>
Wed, 8 Feb 2017 13:55:08 +0000 (14:55 +0100)
src/dvr/dvr.h
src/dvr/dvr_db.c
src/dvr/dvr_rec.c
src/dvr/dvr_vfsmgr.c
src/htsp_server.c
src/streaming.c
src/tvheadend.h

index c802512612f0cc5e0c447a095a4beb327c915d8b..f2504118b4a8abc784a2a390b65c175f1000116c 100644 (file)
@@ -625,6 +625,7 @@ void dvr_spawn_cmd(dvr_entry_t *de, const char *cmd, const char *filename, int p
 void dvr_vfs_refresh_entry(dvr_entry_t *de);
 void dvr_vfs_remove_entry(dvr_entry_t *de);
 int64_t dvr_vfs_update_filename(const char *filename, htsmsg_t *fdata);
+int64_t dvr_vfs_rec_start_check(dvr_config_t *cfg);
 
 void dvr_disk_space_boot(void);
 void dvr_disk_space_init(void);
index c31f14bee94d71b640183275803b2c94bf5ab8b4..6bb59d31d544749758ece0b75812d807a498a4c9 100644 (file)
@@ -645,6 +645,8 @@ dvr_entry_status(dvr_entry_t *de)
         return N_("User access error");
       case SM_CODE_USER_LIMIT:
         return N_("User limit reached");
+      case SM_CODE_NO_SPACE:
+        return streaming_code2txt(de->de_last_error);
       default:
         break;
     }
@@ -658,7 +660,7 @@ dvr_entry_status(dvr_entry_t *de)
       return N_("Completed OK");
 
   case DVR_MISSED_TIME:
-    if (de->de_last_error == SM_CODE_SVC_NOT_ENABLED)
+    if (de->de_last_error == SM_CODE_SVC_NOT_ENABLED || de->de_last_error == SM_CODE_NO_SPACE)
       return streaming_code2txt(de->de_last_error);
     return N_("Time missed");
 
index 5417214f1c1897fcca286197887437e4e741640b..24d70c34efc140e28669174f22362a620ed997ae 100644 (file)
@@ -929,6 +929,11 @@ dvr_rec_start(dvr_entry_t *de, const streaming_start_t *ss)
     return -1;
   }
 
+  if (!dvr_vfs_rec_start_check(cfg)) {
+    dvr_rec_fatal_error(de, "Not enough free disk space");
+    return SM_CODE_NO_SPACE;
+  }
+
   if (!(muxer = prch->prch_muxer)) {
     if (profile_chain_reopen(prch, &cfg->dvr_muxcnf, 0)) {
       dvr_rec_fatal_error(de, "Unable to reopen muxer");
@@ -1191,11 +1196,12 @@ dvr_thread_rec_start(dvr_entry_t **_de, streaming_start_t *ss,
     if (!dvr_thread_global_lock(de, run))
       return 0;
     dvr_rec_set_state(de, DVR_RS_WAIT_PROGRAM_START, 0);
-    if(dvr_rec_start(de, ss) == 0) {
+    int code = dvr_rec_start(de, ss);
+    if(code == 0) {
       ret = 1;
       *started = 1;
     } else
-      dvr_stop_recording(de, SM_CODE_INVALID_TARGET, 1, 0);
+      dvr_stop_recording(de, code == SM_CODE_NO_SPACE ? SM_CODE_NO_SPACE : SM_CODE_INVALID_TARGET, 1, 0);
     dvr_thread_global_unlock(de);
   }
   return ret;
index a5ae2d7eebcbc421418495b186b02b7dc6e4f55f..a64bc7853fc22af60aa1a0bdc48e276c960bbecc 100644 (file)
@@ -174,7 +174,7 @@ dvr_vfs_update_filename(const char *filename, htsmsg_t *fdata)
  * Only "Keep until space needed" recordings are deleted, starting with the oldest one
  */
 static int64_t
-dvr_disk_space_cleanup(dvr_config_t *cfg)
+dvr_disk_space_cleanup(dvr_config_t *cfg, int include_active)
 {
   dvr_entry_t *de, *oldest;
   time_t stoptime;
@@ -264,8 +264,15 @@ dvr_disk_space_cleanup(dvr_config_t *cfg)
       dvr_disk_space_config_lastdelete = mclk();
       dvr_entry_cancel_remove(oldest, 0); /* Remove stored files and mark as "removed" */
     } else {
-      tvhwarn(LS_DVR, "%s \"until space needed\" recordings found for config \"%s\", you are running out of disk space very soon!",
-              loops > 0 ? "Not enough" : "No", configName);
+      /* Stop active recordings if cleanup is not possible */
+      if (loops == 0 && include_active) {
+        tvhwarn(LS_DVR, "No \"until space needed\" recordings found for config \"%s\", aborting active recordings now!", configName);
+        LIST_FOREACH(de, &dvrentries, de_global_link) {
+          if (de->de_sched_state != DVR_RECORDING || !de->de_config || de->de_config != cfg)
+            continue;
+          dvr_stop_recording(de, SM_CODE_NO_SPACE, 1, 0);
+        }
+      }
       goto finish;
     }
 
@@ -336,7 +343,7 @@ dvr_disk_space_check()
           }
 
           /* only cleanup one directory at the time as the system needs time to delete the actual files */
-          dvr_disk_space_cleanup(de->de_config);
+          dvr_disk_space_cleanup(de->de_config, 1);
           cleanupDone = 1;
           dvr_disk_space_config_idx = idx;
           break;
@@ -423,6 +430,37 @@ dvr_get_disk_space_cb(void *aux)
   mtimer_arm_rel(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, sec2mono(15));
 }
 
+/**
+ * Returns the available disk space for a new recording.
+ * If '0' (= below configured minimum), a new recording should not be started.
+ */
+int64_t
+dvr_vfs_rec_start_check(dvr_config_t *cfg)
+{
+  struct statvfs diskdata;
+  dvr_vfs_t *dvfs;
+  int64_t availBytes, requiredBytes, usedBytes, maximalBytes, cleanedBytes;
+
+  lock_assert(&global_lock);
+  if (!cfg || !cfg->dvr_enabled || statvfs(cfg->dvr_storage, &diskdata) == -1)
+    return 0;
+  availBytes    = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
+  requiredBytes = MIB(cfg->dvr_cleanup_threshold_free);
+  maximalBytes  = MIB(cfg->dvr_cleanup_threshold_used);
+  dvfs          = dvr_vfs_find(NULL, tvh_fsid(diskdata.f_fsid));
+  usedBytes     = dvfs->used_size;
+
+  if (availBytes < requiredBytes || ((maximalBytes < usedBytes) && cfg->dvr_cleanup_threshold_used)) {
+    /* Not enough space to start recording, check if cleanup helps */
+    cleanedBytes = dvr_disk_space_cleanup(cfg, 0);
+    availBytes += cleanedBytes;
+    usedBytes -= cleanedBytes;
+    if (availBytes < requiredBytes || ((maximalBytes < usedBytes) && cfg->dvr_cleanup_threshold_used))
+      return 0;
+  }
+  return availBytes;
+}
+
 /**
  *
  */
index 3148c55395686e12fda9cce1303b96980eeddb4f..1385336349ca12ef0be627ef20514ca9bfa15cec 100644 (file)
@@ -4138,6 +4138,8 @@ _htsp_get_subscription_status(int smcode)
     return "userLimit";
   case SM_CODE_WEAK_STREAM:
     return "weakStream";
+  case SM_CODE_NO_SPACE:
+    return "noDiskSpace";
   default:
     return streaming_code2txt(smcode);
   }
index 827ab366fd326657b23f64143201fe56e6a0f0a7..873a104a251339c15f50f958a862483980f5fecb 100644 (file)
@@ -481,6 +481,8 @@ streaming_code2txt(int code)
     return N_("No access");
   case SM_CODE_NO_INPUT:
     return N_("No input detected");
+  case SM_CODE_NO_SPACE:
+    return N_("Not enough disk space");
 
   default:
     snprintf(ret, sizeof(ret), _("Unknown reason (%i)"), code);
index c34e1f2130bca306700d84a610d2b9f48177ccbe..f147a5a1964b690ebcd902490cf8ef622c6847c1 100644 (file)
@@ -537,6 +537,7 @@ typedef enum {
 #define SM_CODE_NO_DESCRAMBLER            400
 #define SM_CODE_NO_ACCESS                 401
 #define SM_CODE_NO_INPUT                  402
+#define SM_CODE_NO_SPACE                  403
 
 typedef enum
 {