/* * Get current / new file */ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) { int fd; struct timespec tp; timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp; timeshift_index_data_t *ti; char path[512]; time_t time; /* Return last file */ if (!create) return timeshift_filemgr_newest(ts); /* No space */ if (ts->full) return NULL; /* Store to file */ clock_gettime(CLOCK_MONOTONIC_COARSE, &tp); time = tp.tv_sec / TIMESHIFT_FILE_PERIOD; tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list); if (!tsf_tl || tsf_tl->time != time) { tsf_hd = TAILQ_FIRST(&ts->files); /* Close existing */ if (tsf_tl && tsf_tl->fd != -1) timeshift_filemgr_close(tsf_tl); /* Check period */ if (ts->max_time && tsf_hd && tsf_tl) { time_t d = (tsf_tl->time - tsf_hd->time) * TIMESHIFT_FILE_PERIOD; if (d > (ts->max_time+5)) { if (!tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); tsf_hd = NULL; } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } } /* Check size */ if (!timeshift_unlimited_size && atomic_pre_add_u64(×hift_total_size, 0) >= timeshift_max_size) { /* Remove the last file (if we can) */ if (tsf_hd && !tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); /* Full */ } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } /* Create new file */ tsf_tmp = NULL; if (!ts->full) { /* Create directories */ if (!ts->path) { if (timeshift_filemgr_makedirs(ts->id, path, sizeof(path))) return NULL; ts->path = strdup(path); } /* Create File */ snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, time); tvhtrace("timeshift", "ts %d create file %s", ts->id, path); if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) { tsf_tmp = calloc(1, sizeof(timeshift_file_t)); tsf_tmp->time = time; tsf_tmp->fd = fd; tsf_tmp->path = strdup(path); tsf_tmp->refcount = 0; tsf_tmp->last = getmonoclock(); TAILQ_INIT(&tsf_tmp->iframes); TAILQ_INIT(&tsf_tmp->sstart); TAILQ_INSERT_TAIL(&ts->files, tsf_tmp, link); /* Copy across last start message */ if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) { tvhtrace("timeshift", "ts %d copy smt_start to new file", ts->id); timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t)); ti2->data = streaming_msg_clone(ti->data); TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); } } } tsf_tl = tsf_tmp; } if (tsf_tl) tsf_tl->refcount++; return tsf_tl; }
/* * Get current / new file */ timeshift_file_t *timeshift_filemgr_get ( timeshift_t *ts, int create ) { int fd; struct timespec tp; timeshift_file_t *tsf_tl, *tsf_hd, *tsf_tmp; timeshift_index_data_t *ti; char path[PATH_MAX]; time_t time; /* Return last file */ if (!create) return timeshift_filemgr_newest(ts); /* No space */ if (ts->full) return NULL; /* Store to file */ clock_gettime(CLOCK_MONOTONIC_COARSE, &tp); time = tp.tv_sec / TIMESHIFT_FILE_PERIOD; tsf_tl = TAILQ_LAST(&ts->files, timeshift_file_list); if (!tsf_tl || tsf_tl->time != time || (tsf_tl->ram && tsf_tl->woff >= timeshift_conf.ram_segment_size)) { tsf_hd = TAILQ_FIRST(&ts->files); /* Close existing */ if (tsf_tl) timeshift_filemgr_close(tsf_tl); /* Check period */ if (!timeshift_conf.unlimited_period && ts->max_time && tsf_hd && tsf_tl) { time_t d = (tsf_tl->time - tsf_hd->time) * TIMESHIFT_FILE_PERIOD; if (d > (ts->max_time+5)) { if (!tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); tsf_hd = NULL; } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } } /* Check size */ if (!timeshift_conf.unlimited_size && atomic_pre_add_u64(×hift_conf.total_size, 0) >= timeshift_conf.max_size) { /* Remove the last file (if we can) */ if (tsf_hd && !tsf_hd->refcount) { timeshift_filemgr_remove(ts, tsf_hd, 0); /* Full */ } else { tvhlog(LOG_DEBUG, "timeshift", "ts %d buffer full", ts->id); ts->full = 1; } } /* Create new file */ tsf_tmp = NULL; if (!ts->full) { tvhtrace("timeshift", "ts %d RAM total %"PRId64" requested %"PRId64" segment %"PRId64, ts->id, atomic_pre_add_u64(×hift_total_ram_size, 0), timeshift_conf.ram_size, timeshift_conf.ram_segment_size); if (timeshift_conf.ram_size >= 8*1024*1024 && atomic_pre_add_u64(×hift_total_ram_size, 0) < timeshift_conf.ram_size + (timeshift_conf.ram_segment_size / 2)) { tsf_tmp = timeshift_filemgr_file_init(ts, time); tsf_tmp->ram_size = MIN(16*1024*1024, timeshift_conf.ram_segment_size); tsf_tmp->ram = malloc(tsf_tmp->ram_size); if (!tsf_tmp->ram) { free(tsf_tmp); tsf_tmp = NULL; } else { tvhtrace("timeshift", "ts %d create RAM segment with %"PRId64" bytes (time %li)", ts->id, tsf_tmp->ram_size, (long)time); } } if (!tsf_tmp && !timeshift_conf.ram_only) { /* Create directories */ if (!ts->path) { if (timeshift_filemgr_makedirs(ts->id, path, sizeof(path))) return NULL; ts->path = strdup(path); } /* Create File */ snprintf(path, sizeof(path), "%s/tvh-%"PRItime_t, ts->path, time); tvhtrace("timeshift", "ts %d create file %s", ts->id, path); if ((fd = open(path, O_WRONLY | O_CREAT, 0600)) > 0) { tsf_tmp = timeshift_filemgr_file_init(ts, time); tsf_tmp->wfd = fd; tsf_tmp->path = strdup(path); } } if (tsf_tmp) { /* Copy across last start message */ if (tsf_tl && (ti = TAILQ_LAST(&tsf_tl->sstart, timeshift_index_data_list))) { tvhtrace("timeshift", "ts %d copy smt_start to new file", ts->id); timeshift_index_data_t *ti2 = calloc(1, sizeof(timeshift_index_data_t)); ti2->data = streaming_msg_clone(ti->data); TAILQ_INSERT_TAIL(&tsf_tmp->sstart, ti2, link); } } } tsf_tl = tsf_tmp; } if (tsf_tl) tsf_tl->refcount++; return tsf_tl; }