mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
Support for more than one video directory
This commit is contained in:
parent
92096e097a
commit
0f2099b4f2
@ -5,6 +5,7 @@ Carsten Koch <Carsten.Koch@icem.de>
|
|||||||
for making the 'Recordings' menu be listed alphabetically
|
for making the 'Recordings' menu be listed alphabetically
|
||||||
for implementing the 'Summary' feature
|
for implementing the 'Summary' feature
|
||||||
for adding the 'epg2timers' tool (see Tools/epg2timers)
|
for adding the 'epg2timers' tool (see Tools/epg2timers)
|
||||||
|
for his idea of using multiple disks (and for testing this feature)
|
||||||
|
|
||||||
Plamen Ganev <pganev@com-it.net>
|
Plamen Ganev <pganev@com-it.net>
|
||||||
for fixing the frequency offset for Hotbird channels
|
for fixing the frequency offset for Hotbird channels
|
||||||
|
5
HISTORY
5
HISTORY
@ -100,9 +100,12 @@ Video Disk Recorder Revision History
|
|||||||
the 'timers.conf' file with a text editor, or by defining/modifying the timer
|
the 'timers.conf' file with a text editor, or by defining/modifying the timer
|
||||||
via the SVDRP interface.
|
via the SVDRP interface.
|
||||||
|
|
||||||
2000-07-28:
|
2000-07-29:
|
||||||
|
|
||||||
- When scrolling through a list it now moves a full page up or down when the
|
- When scrolling through a list it now moves a full page up or down when the
|
||||||
cursor reaches the top or bottom of the menu (thanks to Heino Goldenstein!).
|
cursor reaches the top or bottom of the menu (thanks to Heino Goldenstein!).
|
||||||
- Added missing '#include <sys/stat.h>' to recording.c.
|
- Added missing '#include <sys/stat.h>' to recording.c.
|
||||||
- The video directory can now be defined with the command line option -v.
|
- The video directory can now be defined with the command line option -v.
|
||||||
|
- There can now be more than one video directory (in case you have several
|
||||||
|
disks).
|
||||||
|
- Fixed learning key codes for PC keyboard.
|
||||||
|
34
INSTALL
34
INSTALL
@ -49,6 +49,9 @@ If the program shall run as a daemon, use the --daemon option. This
|
|||||||
will completely detach it from the terminal and will continue as a
|
will completely detach it from the terminal and will continue as a
|
||||||
background process.
|
background process.
|
||||||
|
|
||||||
|
Command line options:
|
||||||
|
---------------------
|
||||||
|
|
||||||
Use "vdr --help" for a list of available command line options.
|
Use "vdr --help" for a list of available command line options.
|
||||||
|
|
||||||
The video data directory:
|
The video data directory:
|
||||||
@ -57,14 +60,41 @@ The video data directory:
|
|||||||
All recordings are written into directories below "/video". Please
|
All recordings are written into directories below "/video". Please
|
||||||
make sure this directory exists, and that the user who runs the 'vdr'
|
make sure this directory exists, and that the user who runs the 'vdr'
|
||||||
program has read and write access to that directory.
|
program has read and write access to that directory.
|
||||||
If you prefer a different location for your video files, you can change
|
If you prefer a different location for your video files, you can use
|
||||||
the value of 'BaseDir' in recording.c.
|
the '-v' option to change that.
|
||||||
|
|
||||||
Note that the file system need not be 64-bit proof, since the 'vdr'
|
Note that the file system need not be 64-bit proof, since the 'vdr'
|
||||||
program splits video files into chunks of about 1GB. You should use
|
program splits video files into chunks of about 1GB. You should use
|
||||||
a disk with several gigabytes of free space. One GB can store roughly
|
a disk with several gigabytes of free space. One GB can store roughly
|
||||||
half an hour of video data.
|
half an hour of video data.
|
||||||
|
|
||||||
|
If you have more than one disk and don't want to combine them to form
|
||||||
|
one large logical volume, you can set up several video directories as
|
||||||
|
mount points for these disks. All of these directories must have the
|
||||||
|
same basic name and must end with a numeric part, which starts at 0 for
|
||||||
|
the main directory and has increasing values for the rest of the
|
||||||
|
directories. For example
|
||||||
|
|
||||||
|
/video0
|
||||||
|
/video1
|
||||||
|
/video2
|
||||||
|
|
||||||
|
would be a setup with three directories. You can use more than one
|
||||||
|
numeric digit, and the directories need not be directly under '/':
|
||||||
|
|
||||||
|
/mnt/MyVideos/vdr.00
|
||||||
|
/mnt/MyVideos/vdr.01
|
||||||
|
/mnt/MyVideos/vdr.02
|
||||||
|
...
|
||||||
|
/mnt/MyVideos/vdr.11
|
||||||
|
|
||||||
|
would set up twelve disks (wow, what a machine that would be!).
|
||||||
|
|
||||||
|
To use such a multi directory setup, you need to add the '-v' option
|
||||||
|
with the name of the basic directory when running 'vdr':
|
||||||
|
|
||||||
|
vdr -v /video0
|
||||||
|
|
||||||
Configuration files:
|
Configuration files:
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
11
Makefile
11
Makefile
@ -4,9 +4,9 @@
|
|||||||
# See the main source file 'vdr.c' for copyright information and
|
# See the main source file 'vdr.c' for copyright information and
|
||||||
# how to reach the author.
|
# how to reach the author.
|
||||||
#
|
#
|
||||||
# $Id: Makefile 1.5 2000/07/23 11:57:14 kls Exp $
|
# $Id: Makefile 1.6 2000/07/28 14:37:44 kls Exp $
|
||||||
|
|
||||||
OBJS = config.o dvbapi.o interface.o menu.o osd.o recording.o remote.o svdrp.o tools.o vdr.o
|
OBJS = config.o dvbapi.o interface.o menu.o osd.o recording.o remote.o svdrp.o tools.o vdr.o videodir.o
|
||||||
|
|
||||||
ifndef REMOTE
|
ifndef REMOTE
|
||||||
REMOTE = KBD
|
REMOTE = KBD
|
||||||
@ -24,15 +24,16 @@ endif
|
|||||||
all: vdr
|
all: vdr
|
||||||
|
|
||||||
config.o : config.c config.h dvbapi.h interface.h tools.h
|
config.o : config.c config.h dvbapi.h interface.h tools.h
|
||||||
dvbapi.o : dvbapi.c config.h dvbapi.h interface.h tools.h
|
dvbapi.o : dvbapi.c config.h dvbapi.h interface.h tools.h videodir.h
|
||||||
interface.o: interface.c config.h dvbapi.h interface.h remote.h tools.h
|
interface.o: interface.c config.h dvbapi.h interface.h remote.h tools.h
|
||||||
menu.o : menu.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h
|
menu.o : menu.c config.h dvbapi.h interface.h menu.h osd.h recording.h tools.h
|
||||||
osd.o : osd.c config.h dvbapi.h interface.h osd.h tools.h
|
osd.o : osd.c config.h dvbapi.h interface.h osd.h tools.h
|
||||||
vdr.o : vdr.c config.h dvbapi.h interface.h menu.h osd.h recording.h svdrp.h tools.h
|
vdr.o : vdr.c config.h dvbapi.h interface.h menu.h osd.h recording.h svdrp.h tools.h videodir.h
|
||||||
recording.o: recording.c config.h dvbapi.h interface.h recording.h tools.h
|
recording.o: recording.c config.h dvbapi.h interface.h recording.h tools.h videodir.h
|
||||||
remote.o : remote.c remote.h tools.h
|
remote.o : remote.c remote.h tools.h
|
||||||
svdrp.o : svdrp.c svdrp.h config.h interface.h tools.h
|
svdrp.o : svdrp.c svdrp.h config.h interface.h tools.h
|
||||||
tools.o : tools.c tools.h
|
tools.o : tools.c tools.h
|
||||||
|
videodir.o : videodir.c tools.h videodir.h
|
||||||
|
|
||||||
vdr: $(OBJS)
|
vdr: $(OBJS)
|
||||||
g++ -g -O2 $(OBJS) -lncurses -o vdr
|
g++ -g -O2 $(OBJS) -lncurses -o vdr
|
||||||
|
48
dvbapi.c
48
dvbapi.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: dvbapi.c 1.15 2000/07/21 13:18:02 kls Exp $
|
* $Id: dvbapi.c 1.16 2000/07/29 14:49:46 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "dvbapi.h"
|
#include "dvbapi.h"
|
||||||
@ -17,6 +17,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
#include "videodir.h"
|
||||||
|
|
||||||
#define VIDEODEVICE "/dev/video"
|
#define VIDEODEVICE "/dev/video"
|
||||||
|
|
||||||
@ -50,9 +51,12 @@
|
|||||||
// 'signed'), so let's use 1GB for absolute safety (the actual file size
|
// 'signed'), so let's use 1GB for absolute safety (the actual file size
|
||||||
// may be slightly higher because we stop recording only before the next
|
// may be slightly higher because we stop recording only before the next
|
||||||
// 'I' frame, to have a complete Group Of Pictures):
|
// 'I' frame, to have a complete Group Of Pictures):
|
||||||
#define MAXVIDEOFILESIZE (1024*1024*1024)
|
#define MAXVIDEOFILESIZE (1024*1024*1024) // Byte
|
||||||
#define MAXFILESPERRECORDING 255
|
#define MAXFILESPERRECORDING 255
|
||||||
|
|
||||||
|
#define MINFREEDISKSPACE (512) // MB
|
||||||
|
#define DISKCHECKINTERVAL 100 // seconds
|
||||||
|
|
||||||
#define INDEXFILESUFFIX "/index.vdr"
|
#define INDEXFILESUFFIX "/index.vdr"
|
||||||
#define RESUMEFILESUFFIX "/resume.vdr"
|
#define RESUMEFILESUFFIX "/resume.vdr"
|
||||||
#define RECORDFILESUFFIX "/%03d.vdr"
|
#define RECORDFILESUFFIX "/%03d.vdr"
|
||||||
@ -598,6 +602,8 @@ private:
|
|||||||
int recordFile;
|
int recordFile;
|
||||||
uchar tagAudio, tagVideo;
|
uchar tagAudio, tagVideo;
|
||||||
bool ok, synced;
|
bool ok, synced;
|
||||||
|
time_t lastDiskSpaceCheck;
|
||||||
|
bool RunningLowOnDiskSpace(void);
|
||||||
int Synchronize(void);
|
int Synchronize(void);
|
||||||
bool NextFile(void);
|
bool NextFile(void);
|
||||||
virtual int Write(int Max = -1);
|
virtual int Write(int Max = -1);
|
||||||
@ -615,6 +621,7 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
|||||||
recordFile = -1;
|
recordFile = -1;
|
||||||
tagAudio = tagVideo = 0;
|
tagAudio = tagVideo = 0;
|
||||||
ok = synced = false;
|
ok = synced = false;
|
||||||
|
lastDiskSpaceCheck = time(NULL);
|
||||||
if (!fileName)
|
if (!fileName)
|
||||||
return;//XXX find a better way???
|
return;//XXX find a better way???
|
||||||
// Find the highest existing file suffix:
|
// Find the highest existing file suffix:
|
||||||
@ -636,7 +643,20 @@ cRecordBuffer::cRecordBuffer(int *InFile, const char *FileName)
|
|||||||
cRecordBuffer::~cRecordBuffer()
|
cRecordBuffer::~cRecordBuffer()
|
||||||
{
|
{
|
||||||
if (recordFile >= 0)
|
if (recordFile >= 0)
|
||||||
close(recordFile);
|
CloseVideoFile(recordFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cRecordBuffer::RunningLowOnDiskSpace(void)
|
||||||
|
{
|
||||||
|
if (time(NULL) > lastDiskSpaceCheck + DISKCHECKINTERVAL) {
|
||||||
|
uint Free = FreeDiskSpaceMB(fileName);
|
||||||
|
lastDiskSpaceCheck = time(NULL);
|
||||||
|
if (Free < MINFREEDISKSPACE) {
|
||||||
|
dsyslog(LOG_INFO, "low disk space (%d MB, limit is %d MB)", Free, MINFREEDISKSPACE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cRecordBuffer::Synchronize(void)
|
int cRecordBuffer::Synchronize(void)
|
||||||
@ -714,20 +734,22 @@ int cRecordBuffer::Synchronize(void)
|
|||||||
|
|
||||||
bool cRecordBuffer::NextFile(void)
|
bool cRecordBuffer::NextFile(void)
|
||||||
{
|
{
|
||||||
if (recordFile >= 0 && fileSize > MAXVIDEOFILESIZE && pictureType == I_FRAME) {
|
if (recordFile >= 0 && pictureType == I_FRAME) { // every file shall start with an I_FRAME
|
||||||
if (close(recordFile) < 0)
|
if (fileSize > MAXVIDEOFILESIZE || RunningLowOnDiskSpace()) {
|
||||||
LOG_ERROR;
|
if (CloseVideoFile(recordFile) < 0)
|
||||||
// don't return 'false', maybe we can still record into the next file
|
LOG_ERROR;
|
||||||
recordFile = -1;
|
// don't return 'false', maybe we can still record into the next file
|
||||||
fileNumber++;
|
recordFile = -1;
|
||||||
if (fileNumber == 0)
|
fileNumber++;
|
||||||
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
|
if (fileNumber == 0)
|
||||||
fileSize = 0;
|
esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING);
|
||||||
|
fileSize = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (recordFile < 0) {
|
if (recordFile < 0) {
|
||||||
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
|
sprintf(pFileNumber, RECORDFILESUFFIX, fileNumber);
|
||||||
dsyslog(LOG_INFO, "recording to '%s'", fileName);
|
dsyslog(LOG_INFO, "recording to '%s'", fileName);
|
||||||
recordFile = open(fileName, O_RDWR | O_CREAT | O_NONBLOCK, S_IRUSR | S_IWUSR);
|
recordFile = OpenVideoFile(fileName, O_RDWR | O_CREAT | O_NONBLOCK);
|
||||||
if (recordFile < 0) {
|
if (recordFile < 0) {
|
||||||
LOG_ERROR;
|
LOG_ERROR;
|
||||||
return false;
|
return false;
|
||||||
|
42
recording.c
42
recording.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: recording.c 1.14 2000/07/28 12:47:54 kls Exp $
|
* $Id: recording.c 1.15 2000/07/29 14:08:17 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@ -17,6 +17,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
#include "videodir.h"
|
||||||
|
|
||||||
#define RECEXT ".rec"
|
#define RECEXT ".rec"
|
||||||
#define DELEXT ".del"
|
#define DELEXT ".del"
|
||||||
@ -25,40 +26,12 @@
|
|||||||
|
|
||||||
#define SUMMARYFILESUFFIX "/summary.vdr"
|
#define SUMMARYFILESUFFIX "/summary.vdr"
|
||||||
|
|
||||||
#define FINDCMD "find %s -type d -name '%s' | sort -df"
|
#define FINDCMD "find %s -follow -type d -name '%s' 2> /dev/null | sort -df"
|
||||||
|
|
||||||
#define DFCMD "df -m %s"
|
|
||||||
#define MINDISKSPACE 1024 // MB
|
#define MINDISKSPACE 1024 // MB
|
||||||
|
|
||||||
#define DISKCHECKDELTA 300 // seconds between checks for free disk space
|
#define DISKCHECKDELTA 300 // seconds between checks for free disk space
|
||||||
|
|
||||||
const char *VideoDirectory = "/video";
|
|
||||||
|
|
||||||
static bool LowDiskSpace(void)
|
|
||||||
{
|
|
||||||
//TODO Find a simpler way to determine the amount of free disk space!
|
|
||||||
bool result = true;
|
|
||||||
char *cmd = NULL;
|
|
||||||
asprintf(&cmd, DFCMD, VideoDirectory);
|
|
||||||
FILE *p = popen(cmd, "r");
|
|
||||||
if (p) {
|
|
||||||
char *s;
|
|
||||||
while ((s = readline(p)) != NULL) {
|
|
||||||
if (*s == '/') {
|
|
||||||
int available;
|
|
||||||
sscanf(s, "%*s %*d %*d %d", &available);
|
|
||||||
result = available < MINDISKSPACE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pclose(p);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
esyslog(LOG_ERR, "ERROR: can't open pipe for cmd '%s'", cmd);
|
|
||||||
delete cmd;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AssertFreeDiskSpace(void)
|
void AssertFreeDiskSpace(void)
|
||||||
{
|
{
|
||||||
// With every call to this function we try to actually remove
|
// With every call to this function we try to actually remove
|
||||||
@ -66,7 +39,7 @@ void AssertFreeDiskSpace(void)
|
|||||||
// it will get removed during the next call.
|
// it will get removed during the next call.
|
||||||
static time_t LastFreeDiskCheck = 0;
|
static time_t LastFreeDiskCheck = 0;
|
||||||
if (time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA) {
|
if (time(NULL) - LastFreeDiskCheck > DISKCHECKDELTA) {
|
||||||
if (LowDiskSpace()) {
|
if (!VideoFileSpaceAvailable(MINDISKSPACE)) {
|
||||||
// Remove the oldest file that has been "deleted":
|
// Remove the oldest file that has been "deleted":
|
||||||
cRecordings Recordings;
|
cRecordings Recordings;
|
||||||
if (Recordings.Load(true)) {
|
if (Recordings.Load(true)) {
|
||||||
@ -240,10 +213,7 @@ bool cRecording::Delete(void)
|
|||||||
if (strcmp(ext, RECEXT) == 0) {
|
if (strcmp(ext, RECEXT) == 0) {
|
||||||
strncpy(ext, DELEXT, strlen(ext));
|
strncpy(ext, DELEXT, strlen(ext));
|
||||||
isyslog(LOG_INFO, "deleting recording %s", FileName());
|
isyslog(LOG_INFO, "deleting recording %s", FileName());
|
||||||
if (rename(FileName(), NewName) == -1) {
|
result = RenameVideoFile(FileName(), NewName);
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", FileName(), strerror(errno));
|
|
||||||
result = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
delete NewName;
|
delete NewName;
|
||||||
return result;
|
return result;
|
||||||
@ -252,7 +222,7 @@ bool cRecording::Delete(void)
|
|||||||
bool cRecording::Remove(void)
|
bool cRecording::Remove(void)
|
||||||
{
|
{
|
||||||
isyslog(LOG_INFO, "removing recording %s", FileName());
|
isyslog(LOG_INFO, "removing recording %s", FileName());
|
||||||
return RemoveFileOrDir(FileName());
|
return RemoveVideoFile(FileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- cRecordings -----------------------------------------------------------
|
// --- cRecordings -----------------------------------------------------------
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: recording.h 1.8 2000/07/28 12:48:06 kls Exp $
|
* $Id: recording.h 1.9 2000/07/28 13:53:54 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __RECORDING_H
|
#ifndef __RECORDING_H
|
||||||
@ -14,8 +14,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
extern const char *VideoDirectory;
|
|
||||||
|
|
||||||
void AssertFreeDiskSpace(void);
|
void AssertFreeDiskSpace(void);
|
||||||
|
|
||||||
class cRecording : public cListObject {
|
class cRecording : public cListObject {
|
||||||
|
67
tools.c
67
tools.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: tools.c 1.11 2000/07/28 13:22:10 kls Exp $
|
* $Id: tools.c 1.12 2000/07/29 14:02:41 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
@ -145,20 +145,47 @@ bool isnumber(const char *s)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirectoryOk(const char *DirName)
|
#define DFCMD "df -m %s"
|
||||||
|
|
||||||
|
uint FreeDiskSpaceMB(const char *Directory)
|
||||||
|
{
|
||||||
|
//TODO Find a simpler way to determine the amount of free disk space!
|
||||||
|
uint Free = 0;
|
||||||
|
char *cmd = NULL;
|
||||||
|
asprintf(&cmd, DFCMD, Directory);
|
||||||
|
FILE *p = popen(cmd, "r");
|
||||||
|
if (p) {
|
||||||
|
char *s;
|
||||||
|
while ((s = readline(p)) != NULL) {
|
||||||
|
if (*s == '/') {
|
||||||
|
uint available;
|
||||||
|
sscanf(s, "%*s %*d %*d %u", &available);
|
||||||
|
Free = available;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pclose(p);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esyslog(LOG_ERR, "ERROR: can't open pipe for cmd '%s'", cmd);
|
||||||
|
delete cmd;
|
||||||
|
return Free;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DirectoryOk(const char *DirName, bool LogErrors)
|
||||||
{
|
{
|
||||||
struct stat ds;
|
struct stat ds;
|
||||||
if (stat(DirName, &ds) == 0) {
|
if (stat(DirName, &ds) == 0) {
|
||||||
if (S_ISDIR(ds.st_mode)) {
|
if (S_ISDIR(ds.st_mode)) {
|
||||||
if (access(DirName, R_OK | W_OK | X_OK) == 0)
|
if (access(DirName, R_OK | W_OK | X_OK) == 0)
|
||||||
return true;
|
return true;
|
||||||
else
|
else if (LogErrors)
|
||||||
esyslog(LOG_ERR, "ERROR: can't access %s", DirName);
|
esyslog(LOG_ERR, "ERROR: can't access %s", DirName);
|
||||||
}
|
}
|
||||||
else
|
else if (LogErrors)
|
||||||
esyslog(LOG_ERR, "ERROR: %s is not a directory", DirName);
|
esyslog(LOG_ERR, "ERROR: %s is not a directory", DirName);
|
||||||
}
|
}
|
||||||
else
|
else if (LogErrors)
|
||||||
LOG_ERROR_STR(DirName);
|
LOG_ERROR_STR(DirName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -175,7 +202,7 @@ bool MakeDirs(const char *FileName, bool IsDirectory)
|
|||||||
*p = 0;
|
*p = 0;
|
||||||
struct stat fs;
|
struct stat fs;
|
||||||
if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
|
if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
|
||||||
isyslog(LOG_INFO, "creating directory %s", s);
|
dsyslog(LOG_INFO, "creating directory %s", s);
|
||||||
if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
|
if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
|
esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
|
||||||
result = false;
|
result = false;
|
||||||
@ -191,7 +218,7 @@ bool MakeDirs(const char *FileName, bool IsDirectory)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RemoveFileOrDir(const char *FileName)
|
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (stat(FileName, &st) == 0) {
|
if (stat(FileName, &st) == 0) {
|
||||||
@ -203,23 +230,43 @@ bool RemoveFileOrDir(const char *FileName)
|
|||||||
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
|
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
|
||||||
char *buffer;
|
char *buffer;
|
||||||
asprintf(&buffer, "%s/%s", FileName, e->d_name);
|
asprintf(&buffer, "%s/%s", FileName, e->d_name);
|
||||||
|
if (FollowSymlinks) {
|
||||||
|
int size = strlen(buffer) * 2; // should be large enough
|
||||||
|
char *l = new char[size];
|
||||||
|
int n = readlink(buffer, l, size);
|
||||||
|
if (n < 0) {
|
||||||
|
if (errno != EINVAL)
|
||||||
|
LOG_ERROR_STR(buffer);
|
||||||
|
}
|
||||||
|
else if (n < size) {
|
||||||
|
l[n] = 0;
|
||||||
|
dsyslog(LOG_INFO, "removing %s", l);
|
||||||
|
if (remove(l) < 0)
|
||||||
|
LOG_ERROR_STR(l);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
esyslog(LOG_ERR, "symlink name length (%d) exceeded anticipated buffer size (%d)", n, size);
|
||||||
|
delete l;
|
||||||
|
}
|
||||||
|
dsyslog(LOG_INFO, "removing %s", buffer);
|
||||||
if (remove(buffer) < 0)
|
if (remove(buffer) < 0)
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", buffer, strerror(errno));
|
LOG_ERROR_STR(buffer);
|
||||||
delete buffer;
|
delete buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir(d);
|
closedir(d);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", FileName, strerror(errno));
|
LOG_ERROR_STR(FileName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
dsyslog(LOG_INFO, "removing %s", FileName);
|
||||||
if (remove(FileName) == 0)
|
if (remove(FileName) == 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
esyslog(LOG_ERR, "ERROR: %s: %s", FileName, strerror(errno));
|
LOG_ERROR_STR(FileName);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
tools.h
7
tools.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: tools.h 1.11 2000/07/28 13:02:05 kls Exp $
|
* $Id: tools.h 1.12 2000/07/29 10:56:00 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __TOOLS_H
|
#ifndef __TOOLS_H
|
||||||
@ -43,9 +43,10 @@ char *skipspace(char *s);
|
|||||||
int time_ms(void);
|
int time_ms(void);
|
||||||
void delay_ms(int ms);
|
void delay_ms(int ms);
|
||||||
bool isnumber(const char *s);
|
bool isnumber(const char *s);
|
||||||
bool DirectoryOk(const char *DirName);
|
uint FreeDiskSpaceMB(const char *Directory);
|
||||||
|
bool DirectoryOk(const char *DirName, bool LogErrors = false);
|
||||||
bool MakeDirs(const char *FileName, bool IsDirectory = false);
|
bool MakeDirs(const char *FileName, bool IsDirectory = false);
|
||||||
bool RemoveFileOrDir(const char *FileName);
|
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks = false);
|
||||||
bool CheckProcess(pid_t pid);
|
bool CheckProcess(pid_t pid);
|
||||||
void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT);
|
void KillProcess(pid_t pid, int Timeout = MAXPROCESSTIMEOUT);
|
||||||
|
|
||||||
|
5
vdr.c
5
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
* The project's page is at http://www.cadsoft.de/people/kls/vdr
|
||||||
*
|
*
|
||||||
* $Id: vdr.c 1.24 2000/07/28 13:14:19 kls Exp $
|
* $Id: vdr.c 1.25 2000/07/28 15:55:31 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -36,6 +36,7 @@
|
|||||||
#include "recording.h"
|
#include "recording.h"
|
||||||
#include "svdrp.h"
|
#include "svdrp.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
#include "videodir.h"
|
||||||
|
|
||||||
#ifdef REMOTE_KBD
|
#ifdef REMOTE_KBD
|
||||||
#define KEYS_CONF "keys-pc.conf"
|
#define KEYS_CONF "keys-pc.conf"
|
||||||
@ -105,7 +106,7 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
// Check the video directory:
|
// Check the video directory:
|
||||||
|
|
||||||
if (!DirectoryOk(VideoDirectory)) {
|
if (!DirectoryOk(VideoDirectory, true)) {
|
||||||
fprintf(stderr, "vdr: can't access video directory %s\n", VideoDirectory);
|
fprintf(stderr, "vdr: can't access video directory %s\n", VideoDirectory);
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
182
videodir.c
Normal file
182
videodir.c
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* videodir.c: Functions to maintain a distributed video directory
|
||||||
|
*
|
||||||
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
|
* how to reach the author.
|
||||||
|
*
|
||||||
|
* $Id: videodir.c 1.1 2000/07/29 15:21:42 kls Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "videodir.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include "tools.h"
|
||||||
|
|
||||||
|
const char *VideoDirectory = "/video";
|
||||||
|
|
||||||
|
class cVideoDirectory {
|
||||||
|
private:
|
||||||
|
char *name, *stored, *adjusted;
|
||||||
|
int length, number, digits;
|
||||||
|
public:
|
||||||
|
cVideoDirectory(void);
|
||||||
|
~cVideoDirectory();
|
||||||
|
uint FreeMB(void);
|
||||||
|
const char *Name(void) { return name; }
|
||||||
|
const char *Stored(void) { return stored; }
|
||||||
|
int Length(void) { return length; }
|
||||||
|
bool IsDistributed(void) { return name != NULL; }
|
||||||
|
bool Next(void);
|
||||||
|
void Store(void);
|
||||||
|
const char *Adjust(const char *FileName);
|
||||||
|
};
|
||||||
|
|
||||||
|
cVideoDirectory::cVideoDirectory(void)
|
||||||
|
{
|
||||||
|
length = strlen(VideoDirectory);
|
||||||
|
name = (VideoDirectory[length - 1] == '0') ? strdup(VideoDirectory) : NULL;
|
||||||
|
stored = adjusted = NULL;
|
||||||
|
number = -1;
|
||||||
|
digits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
cVideoDirectory::~cVideoDirectory()
|
||||||
|
{
|
||||||
|
delete name;
|
||||||
|
delete stored;
|
||||||
|
delete adjusted;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint cVideoDirectory::FreeMB(void)
|
||||||
|
{
|
||||||
|
return FreeDiskSpaceMB(name ? name : VideoDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cVideoDirectory::Next(void)
|
||||||
|
{
|
||||||
|
if (name) {
|
||||||
|
if (number < 0) {
|
||||||
|
int l = length;
|
||||||
|
while (l-- > 0 && isdigit(name[l]))
|
||||||
|
;
|
||||||
|
l++;
|
||||||
|
digits = length - l;
|
||||||
|
int n = atoi(&name[l]);
|
||||||
|
if (n == 0)
|
||||||
|
number = n;
|
||||||
|
else
|
||||||
|
return false; // base video directory must end with zero
|
||||||
|
}
|
||||||
|
if (++number > 0) {
|
||||||
|
char buf[16];
|
||||||
|
if (sprintf(buf, "%0*d", digits, number) == digits) {
|
||||||
|
strcpy(&name[length - digits], buf);
|
||||||
|
return DirectoryOk(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cVideoDirectory::Store(void)
|
||||||
|
{
|
||||||
|
if (name) {
|
||||||
|
delete stored;
|
||||||
|
stored = strdup(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *cVideoDirectory::Adjust(const char *FileName)
|
||||||
|
{
|
||||||
|
if (stored) {
|
||||||
|
delete adjusted;
|
||||||
|
adjusted = strdup(FileName);
|
||||||
|
return strncpy(adjusted, stored, length);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int OpenVideoFile(const char *FileName, int Flags)
|
||||||
|
{
|
||||||
|
const char *ActualFileName = FileName;
|
||||||
|
|
||||||
|
// Incoming name must be in base video directory:
|
||||||
|
if (strstr(FileName, VideoDirectory) != FileName) {
|
||||||
|
esyslog(LOG_ERR, "ERROR: %s not in %s", FileName, VideoDirectory);
|
||||||
|
errno = ENOENT; // must set 'errno' - any ideas for a better value?
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Are we going to create a new file?
|
||||||
|
if ((Flags & O_CREAT) != 0) {
|
||||||
|
cVideoDirectory Dir;
|
||||||
|
if (Dir.IsDistributed()) {
|
||||||
|
// Find the directory with the most free space:
|
||||||
|
uint MaxFree = Dir.FreeMB();
|
||||||
|
while (Dir.Next()) {
|
||||||
|
uint Free = FreeDiskSpaceMB(Dir.Name());
|
||||||
|
if (Free > MaxFree) {
|
||||||
|
Dir.Store();
|
||||||
|
MaxFree = Free;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Dir.Stored()) {
|
||||||
|
ActualFileName = Dir.Adjust(FileName);
|
||||||
|
if (!MakeDirs(ActualFileName, false))
|
||||||
|
return -1; // errno has been set by MakeDirs()
|
||||||
|
if (symlink(ActualFileName, FileName) < 0) {
|
||||||
|
LOG_ERROR_STR(FileName);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ActualFileName = strdup(ActualFileName); // must survive Dir!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int Result = open(ActualFileName, Flags, S_IRUSR | S_IWUSR);
|
||||||
|
if (ActualFileName != FileName)
|
||||||
|
delete ActualFileName;
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CloseVideoFile(int FileHandle)
|
||||||
|
{
|
||||||
|
// just in case we ever decide to do something special when closing the file!
|
||||||
|
return close(FileHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RenameVideoFile(const char *OldName, const char *NewName)
|
||||||
|
{
|
||||||
|
// Only the base video directory entry will be renamed, leaving the
|
||||||
|
// possible symlinks untouched. Going through all the symlinks and disks
|
||||||
|
// would be unnecessary work - maybe later...
|
||||||
|
if (rename(OldName, NewName) == -1) {
|
||||||
|
LOG_ERROR_STR(OldName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoveVideoFile(const char *FileName)
|
||||||
|
{
|
||||||
|
return RemoveFileOrDir(FileName, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VideoFileSpaceAvailable(unsigned int SizeMB)
|
||||||
|
{
|
||||||
|
cVideoDirectory Dir;
|
||||||
|
if (Dir.IsDistributed()) {
|
||||||
|
if (Dir.FreeMB() >= SizeMB * 2) // base directory needs additional space
|
||||||
|
return true;
|
||||||
|
while (Dir.Next()) {
|
||||||
|
if (Dir.FreeMB() >= SizeMB)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Dir.FreeMB() >= SizeMB;
|
||||||
|
}
|
21
videodir.h
Normal file
21
videodir.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* videodir.h: Functions to maintain a distributed video directory
|
||||||
|
*
|
||||||
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
|
* how to reach the author.
|
||||||
|
*
|
||||||
|
* $Id: videodir.h 1.1 2000/07/29 14:08:27 kls Exp $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __VIDEODIR_H
|
||||||
|
#define __VIDEODIR_H
|
||||||
|
|
||||||
|
extern const char *VideoDirectory;
|
||||||
|
|
||||||
|
int OpenVideoFile(const char *FileName, int Flags);
|
||||||
|
int CloseVideoFile(int FileHandle);
|
||||||
|
bool RenameVideoFile(const char *OldName, const char *NewName);
|
||||||
|
bool RemoveVideoFile(const char *FileName);
|
||||||
|
bool VideoFileSpaceAvailable(unsigned int SizeMB);
|
||||||
|
|
||||||
|
#endif //__VIDEODIR_H
|
Loading…
Reference in New Issue
Block a user