mirror of
				https://github.com/vdr-projects/vdr.git
				synced 2025-03-01 10:50:46 +00:00 
			
		
		
		
	Support for more than one video directory
This commit is contained in:
		| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										34
									
								
								dvbapi.c
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								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,8 +734,9 @@ 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()) { | ||||||
|  |         if (CloseVideoFile(recordFile) < 0) | ||||||
|            LOG_ERROR; |            LOG_ERROR; | ||||||
|            // don't return 'false', maybe we can still record into the next file |            // don't return 'false', maybe we can still record into the next file | ||||||
|         recordFile = -1; |         recordFile = -1; | ||||||
| @@ -724,10 +745,11 @@ bool cRecordBuffer::NextFile(void) | |||||||
|            esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING); |            esyslog(LOG_ERR, "ERROR: max number of files (%d) exceeded", MAXFILESPERRECORDING); | ||||||
|         fileSize = 0; |         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 | ||||||
		Reference in New Issue
	
	Block a user