Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
DVR: move disk space code to vfsmgr
  • Loading branch information
perexg committed Dec 3, 2015
1 parent bba94ab commit 4d0738e
Show file tree
Hide file tree
Showing 3 changed files with 315 additions and 288 deletions.
3 changes: 2 additions & 1 deletion Makefile
Expand Up @@ -275,8 +275,9 @@ SRCS-2 += \
src/dvr/dvr_rec.c \
src/dvr/dvr_autorec.c \
src/dvr/dvr_timerec.c \
src/dvr/dvr_vfsmgr.c \
src/dvr/dvr_config.c \
src/dvr/dvr_cutpoints.c \
src/dvr/dvr_cutpoints.c

SRCS-2 += \
src/webui/webui.c \
Expand Down
287 changes: 0 additions & 287 deletions src/dvr/dvr_rec.c
Expand Up @@ -33,23 +33,13 @@
#include "dvr.h"
#include "spawn.h"
#include "service.h"
#include "plumbing/tsfix.h"
#include "plumbing/globalheaders.h"
#include "htsp_server.h"
#include "atomic.h"
#include "intlconv.h"
#include "notify.h"

#include "muxer.h"

#if ENABLE_ANDROID
#include <sys/vfs.h>
#define statvfs statfs
#define fstatvfs fstatfs
#else
#include <sys/statvfs.h>
#endif

/**
*
*/
Expand Down Expand Up @@ -1537,280 +1527,3 @@ dvr_thread_epilog(dvr_entry_t *de, const char *dvr_postproc)
if(dvr_postproc && dvr_postproc[0])
dvr_spawn_postcmd(de, dvr_postproc, NULL);
}

/**
*
*/
static int dvr_disk_space_config_idx;
static int dvr_disk_space_config_size;
static time_t dvr_disk_space_config_lastdelete;
static int64_t dvr_bfree;
static int64_t dvr_btotal;
static pthread_mutex_t dvr_disk_space_mutex;
static gtimer_t dvr_disk_space_timer;
static tasklet_t dvr_disk_space_tasklet;

/**
* Cleanup old recordings for this config until the dvr_cleanup_threshold is reached
* 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_entry_t *de, *oldest;
time_t stoptime;
int64_t requiredBytes, availBytes;
int64_t clearedBytes = 0, fileSize;
unsigned long int filesystemId;
struct statvfs diskdata;
struct tm tm;
int loops = 0;
char tbuf[64];
const char *configName;

if (!cfg || !cfg->dvr_enabled || statvfs(cfg->dvr_storage, &diskdata) == -1)
return -1;

filesystemId = diskdata.f_fsid;
availBytes = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;
configName = cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile";

/* When deleting a file from the disk, the system needs some time to actually do this */
/* If calling this function to fast after the previous call, statvfs might be wrong/not updated yet */
/* So we are risking to delete more files than needed, so allow 10s for the system to handle previous deletes */
if (dvr_disk_space_config_lastdelete + 10 > dispatch_clock) {
tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\" is not allowed now", configName);
return -1;
}

if (diskdata.f_bsize * (int64_t)diskdata.f_blocks < requiredBytes) {
tvhlog(LOG_WARNING, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\" is smaller than the total disk size!",
configName, requiredBytes/(int64_t)1024/(int64_t)1024);
return -1;
}

tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
configName, requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);

while (availBytes < requiredBytes) {
oldest = NULL;
stoptime = dispatch_clock;

LIST_FOREACH(de, &dvrentries, de_global_link) {
if (de->de_sched_state != DVR_COMPLETED &&
de->de_sched_state != DVR_MISSED_TIME)
continue;

if (dvr_entry_get_stop_time(de) > stoptime)
continue;

if (dvr_entry_get_removal_days(de) != DVR_RET_SPACENEED) // only remove the allowed ones
continue;

if (dvr_get_filename(de) == NULL || dvr_get_filesize(de) <= 0)
continue;

if(statvfs(dvr_get_filename(de), &diskdata) == -1)
continue;

/* Checking for the same config is useless as it's storage path might be changed meanwhile */
/* Check for the same file system instead */
if (filesystemId == 0 || diskdata.f_fsid != filesystemId)
continue;

oldest = de; // the oldest one until now
stoptime = dvr_entry_get_stop_time(de);
}

if (oldest) {
fileSize = dvr_get_filesize(oldest);
availBytes += fileSize;
clearedBytes += fileSize;

localtime_r(&stoptime, &tm);
if (strftime(tbuf, sizeof(tbuf), "%F %T", &tm) <= 0)
*tbuf = 0;
tvhlog(LOG_INFO, "dvr","Delete \"until space needed\" recording \"%s\" with stop time \"%s\" and file size \"%ld MB\"",
lang_str_get(oldest->de_title, NULL), tbuf, fileSize/(int64_t)1024/(int64_t)1024);

dvr_disk_space_config_lastdelete = dispatch_clock;
dvr_entry_delete(oldest, 1); // delete actual file
if (dvr_entry_get_retention_days(oldest) == DVR_RET_ONREMOVE)
dvr_entry_destroy(oldest, 1); // also delete database entry
}
else {
tvhlog(LOG_WARNING, "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);
goto finish;
}

loops++;
if (loops >= 10) {
tvhlog(LOG_WARNING, "dvr","Not able to clear the required disk space after deleting %i \"until space needed\" recordings...", loops);
goto finish;
}
}

finish:
tvhlog(LOG_INFO, "dvr","disk space cleanup for config \"%s\", cleared \"%ld MB\" of disk space, new free disk space \"%ld MB\"",
configName, clearedBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);

return clearedBytes;
}

/**
* Check for each dvr config if the free disk size is below the dvr_cleanup_threshold
* If so and we are using the dvr config ATM (active recording), we start the cleanup procedure
*/
static void
dvr_disk_space_check()
{
dvr_config_t *cfg;
dvr_entry_t *de;
struct statvfs diskdata;
int64_t requiredBytes, availBytes;
int idx = 0, cleanupDone = 0;

dvr_disk_space_config_idx++;
if (dvr_disk_space_config_idx > dvr_disk_space_config_size)
dvr_disk_space_config_idx = 1;

LIST_FOREACH(cfg, &dvrconfigs, config_link)
{
idx++;

if (cfg->dvr_enabled &&
!cleanupDone &&
idx >= dvr_disk_space_config_idx &&
statvfs(cfg->dvr_storage, &diskdata) != -1)
{
availBytes = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
requiredBytes = (int64_t)cfg->dvr_cleanup_threshold*(int64_t)1024*(int64_t)1024;

if (availBytes < requiredBytes) {
LIST_FOREACH(de, &dvrentries, de_global_link) {

/* only start cleanup if we are actually writing files right now */
if (de->de_sched_state != DVR_RECORDING || !de->de_config || de->de_config != cfg)
continue;

tvhlog(LOG_WARNING, "dvr","running out of free disk space for dvr config \"%s\", required free space \"%ld MB\", current free space \"%ld MB\"",
cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile",
requiredBytes/(int64_t)1024/(int64_t)1024, availBytes/(int64_t)1024/(int64_t)1024);

/* only cleanup one directory at the time as the system needs time to delete the actual files */
dvr_disk_space_cleanup(de->de_config);
cleanupDone = 1;
dvr_disk_space_config_idx = idx;
break;
}
}
else {
tvhlog(LOG_DEBUG, "dvr","checking free disk space for config \"%s\" : OK",
cfg != dvr_config_find_by_name(NULL) ? cfg->dvr_config_name : "Default profile");
}
}
}

if (!cleanupDone)
dvr_disk_space_config_idx = 0;

dvr_disk_space_config_size = idx;
}
/**
*
*/
static void
dvr_get_disk_space_update(const char *path)
{
struct statvfs diskdata;

if(statvfs(path, &diskdata) == -1)
return;

dvr_bfree = diskdata.f_bsize * (int64_t)diskdata.f_bavail;
dvr_btotal = diskdata.f_bsize * (int64_t)diskdata.f_blocks;
}

/**
*
*/
static void
dvr_get_disk_space_tcb(void *opaque, int dearmed)
{
if (!dearmed) {
htsmsg_t *m = htsmsg_create_map();
pthread_mutex_lock(&dvr_disk_space_mutex);

/* update disk space from default dvr config */
dvr_get_disk_space_update((char *)opaque);
htsmsg_add_s64(m, "freediskspace", dvr_bfree);
htsmsg_add_s64(m, "totaldiskspace", dvr_btotal);

/* check free disk space for each dvr config and start cleanup if needed */
dvr_disk_space_check();
pthread_mutex_unlock(&dvr_disk_space_mutex);

notify_by_msg("diskspaceUpdate", m, 0);
}

free(opaque);
}

static void
dvr_get_disk_space_cb(void *aux)
{
dvr_config_t *cfg;
char *path;

lock_assert(&global_lock);

cfg = dvr_config_find_by_name_default(NULL);
if (cfg->dvr_storage && cfg->dvr_storage[0]) {
path = strdup(cfg->dvr_storage);
tasklet_arm(&dvr_disk_space_tasklet, dvr_get_disk_space_tcb, path);
}
gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 15);
}

/**
*
*/
void
dvr_disk_space_init(void)
{
dvr_config_t *cfg = dvr_config_find_by_name_default(NULL);
pthread_mutex_init(&dvr_disk_space_mutex, NULL);
dvr_get_disk_space_update(cfg->dvr_storage);
gtimer_arm(&dvr_disk_space_timer, dvr_get_disk_space_cb, NULL, 5);
}

/**
*
*/
void
dvr_disk_space_done(void)
{
tasklet_disarm(&dvr_disk_space_tasklet);
gtimer_disarm(&dvr_disk_space_timer);
}

/**
*
*/
int
dvr_get_disk_space(int64_t *bfree, int64_t *btotal)
{
int res = 0;

pthread_mutex_lock(&dvr_disk_space_mutex);
if (dvr_bfree || dvr_btotal) {
*bfree = dvr_bfree;
*btotal = dvr_btotal;
} else {
res = -EINVAL;
}
pthread_mutex_unlock(&dvr_disk_space_mutex);
return res;
}

0 comments on commit 4d0738e

Please sign in to comment.