vdr/tools.c
Klaus Schmidinger a69b3211dc Version 0.67
- The EIT information is now gathered in a separate thread.
- The sytem time can now be synchronized to the time broadcast in the DVB data
  stream. This can be enabled in the "Setup" menu by setting "SetSystemTime" to
  1.  Note that this works only if VDR is running under a user id that has
  permisson to set the system time.
- The new item "Schedule" in the "Main" menu opens VDR's EPG (thanks to Robert
  Schneider). See the MANUAL file for a detailed description.
- The new setup parameters MarginStart and MarginStop define how long (in
  minutes) before the official start time of a broadcast VDR shall begin
  recording, and how long after the official end time it shall stop recording.
  These are used when a recording is programmed from the "Schedules" menu.
- The delay value in the dvb.c.071.diff patch to the driver has been increased
  to '3', because on some systems the OSD was not displayed correctly. If you
  are running an already patched version 0.71 driver and encounter problems
  with the OSD, please make sure the parameter in the ddelay call is '3', not
  '2'.
- Fixed initializing the RCU remote control code (didn't work after switching
  on the system).
- Problematic characters in recording names (which can come from timers that
  are programmed via the "Schedules" menu) are now replaced by suitable
  substitutes.
2000-11-01 18:00:00 +01:00

550 lines
12 KiB
C

/*
* tools.c: Various tools
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: tools.c 1.22 2000/10/29 11:21:55 kls Exp $
*/
#define _GNU_SOURCE
#include "tools.h"
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#if defined(DEBUG_OSD)
#include <ncurses.h>
#endif
#include <signal.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
#define MaxBuffer 1000
int SysLogLevel = 3;
void writechar(int filedes, char c)
{
write(filedes, &c, sizeof(c));
}
void writeint(int filedes, int n)
{
write(filedes, &n, sizeof(n));
}
char readchar(int filedes)
{
char c;
read(filedes, &c, 1);
return c;
}
bool readint(int filedes, int &n)
{
return cFile::AnyFileReady(filedes, 0) && read(filedes, &n, sizeof(n)) == sizeof(n);
}
void purge(int filedes)
{
while (cFile::AnyFileReady(filedes, 0))
readchar(filedes);
}
char *readline(FILE *f)
{
static char buffer[MaxBuffer];
if (fgets(buffer, sizeof(buffer), f) > 0) {
int l = strlen(buffer) - 1;
if (l >= 0 && buffer[l] == '\n')
buffer[l] = 0;
return buffer;
}
return NULL;
}
char *strn0cpy(char *dest, const char *src, size_t n)
{
char *s = dest;
for ( ; --n && (*dest = *src) != 0; dest++, src++) ;
*dest = 0;
return s;
}
char *strreplace(char *s, char c1, char c2)
{
char *p = s;
while (*p) {
if (*p == c1)
*p = c2;
p++;
}
return s;
}
char *skipspace(const char *s)
{
while (*s && isspace(*s))
s++;
return (char *)s;
}
bool isempty(const char *s)
{
return !(s && *skipspace(s));
}
int time_ms(void)
{
static time_t t0 = 0;
struct timeval t;
if (gettimeofday(&t, NULL) == 0) {
if (t0 == 0)
t0 = t.tv_sec; // this avoids an overflow (we only work with deltas)
return (t.tv_sec - t0) * 1000 + t.tv_usec / 1000;
}
return 0;
}
void delay_ms(int ms)
{
int t0 = time_ms();
while (time_ms() - t0 < ms)
;
}
bool isnumber(const char *s)
{
while (*s) {
if (!isdigit(*s))
return false;
s++;
}
return true;
}
const char *AddDirectory(const char *DirName, const char *FileName)
{
static char *buf = NULL;
delete buf;
asprintf(&buf, "%s/%s", DirName && *DirName ? DirName : ".", FileName);
return buf;
}
#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;
if (stat(DirName, &ds) == 0) {
if (S_ISDIR(ds.st_mode)) {
if (access(DirName, R_OK | W_OK | X_OK) == 0)
return true;
else if (LogErrors)
esyslog(LOG_ERR, "ERROR: can't access %s", DirName);
}
else if (LogErrors)
esyslog(LOG_ERR, "ERROR: %s is not a directory", DirName);
}
else if (LogErrors)
LOG_ERROR_STR(DirName);
return false;
}
bool MakeDirs(const char *FileName, bool IsDirectory)
{
bool result = true;
char *s = strdup(FileName);
char *p = s;
if (*p == '/')
p++;
while ((p = strchr(p, '/')) != NULL || IsDirectory) {
if (p)
*p = 0;
struct stat fs;
if (stat(s, &fs) != 0 || !S_ISDIR(fs.st_mode)) {
dsyslog(LOG_INFO, "creating directory %s", s);
if (mkdir(s, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) {
esyslog(LOG_ERR, "ERROR: %s: %s", s, strerror(errno));
result = false;
break;
}
}
if (p)
*p++ = '/';
else
break;
}
delete s;
return result;
}
bool RemoveFileOrDir(const char *FileName, bool FollowSymlinks)
{
struct stat st;
if (stat(FileName, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
DIR *d = opendir(FileName);
if (d) {
struct dirent *e;
while ((e = readdir(d)) != NULL) {
if (strcmp(e->d_name, ".") && strcmp(e->d_name, "..")) {
char *buffer;
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, "ERROR: symlink name length (%d) exceeded anticipated buffer size (%d)", n, size);
delete l;
}
dsyslog(LOG_INFO, "removing %s", buffer);
if (remove(buffer) < 0)
LOG_ERROR_STR(buffer);
delete buffer;
}
}
closedir(d);
}
else {
LOG_ERROR_STR(FileName);
return false;
}
}
dsyslog(LOG_INFO, "removing %s", FileName);
if (remove(FileName) == 0)
return true;
}
else
LOG_ERROR_STR(FileName);
return false;
}
bool CheckProcess(pid_t pid)
{
pid_t Pid2Check = pid;
int status;
pid = waitpid(Pid2Check, &status, WNOHANG);
if (pid < 0) {
if (errno != ECHILD)
LOG_ERROR;
return false;
}
return true;
}
void KillProcess(pid_t pid, int Timeout)
{
pid_t Pid2Wait4 = pid;
for (time_t t0 = time(NULL); time(NULL) - t0 < Timeout; ) {
int status;
pid_t pid = waitpid(Pid2Wait4, &status, WNOHANG);
if (pid < 0) {
if (errno != ECHILD)
LOG_ERROR;
return;
}
if (pid == Pid2Wait4)
return;
}
esyslog(LOG_ERR, "ERROR: process %d won't end (waited %d seconds) - terminating it...", Pid2Wait4, Timeout);
if (kill(Pid2Wait4, SIGTERM) < 0) {
esyslog(LOG_ERR, "ERROR: process %d won't terminate (%s) - killing it...", Pid2Wait4, strerror(errno));
if (kill(Pid2Wait4, SIGKILL) < 0)
esyslog(LOG_ERR, "ERROR: process %d won't die (%s) - giving up", Pid2Wait4, strerror(errno));
}
}
// --- cFile -----------------------------------------------------------------
bool cFile::files[FD_SETSIZE] = { false };
int cFile::maxFiles = 0;
cFile::cFile(void)
{
f = -1;
}
cFile::~cFile()
{
Close();
}
bool cFile::Open(const char *FileName, int Flags, mode_t Mode)
{
if (!IsOpen())
return Open(open(FileName, Flags, Mode));
esyslog(LOG_ERR, "ERROR: attempt to re-open %s", FileName);
return false;
}
bool cFile::Open(int FileDes)
{
if (FileDes >= 0) {
if (!IsOpen()) {
f = FileDes;
if (f >= 0) {
if (f < FD_SETSIZE) {
if (f >= maxFiles)
maxFiles = f + 1;
if (!files[f])
files[f] = true;
else
esyslog(LOG_ERR, "ERROR: file descriptor %d already in files[]", f);
return true;
}
else
esyslog(LOG_ERR, "ERROR: file descriptor %d is larger than FD_SETSIZE (%d)", f, FD_SETSIZE);
}
}
else
esyslog(LOG_ERR, "ERROR: attempt to re-open file descriptor %d", FileDes);
}
return false;
}
void cFile::Close(void)
{
if (f >= 0) {
close(f);
files[f] = false;
f = -1;
}
}
int cFile::ReadString(char *Buffer, int Size)
{
int rbytes = 0;
bool wait = true;
while (Ready(wait)) {
int n = read(f, Buffer + rbytes, 1);
if (n == 0)
break; // EOF
if (n < 0) {
LOG_ERROR;
return -1;
}
rbytes += n;
if (rbytes == Size || Buffer[rbytes - 1] == '\n')
break;
wait = false;
}
return rbytes;
}
bool cFile::Ready(bool Wait)
{
return f >= 0 && AnyFileReady(f, Wait ? 1000 : 0);
}
bool cFile::AnyFileReady(int FileDes, int TimeoutMs)
{
#ifdef DEBUG_OSD
refresh();
#endif
fd_set set;
FD_ZERO(&set);
for (int i = 0; i < maxFiles; i++) {
if (files[i])
FD_SET(i, &set);
}
if (0 <= FileDes && FileDes < FD_SETSIZE && !files[FileDes])
FD_SET(FileDes, &set); // in case we come in with an arbitrary descriptor
if (TimeoutMs == 0)
TimeoutMs = 10; // load gets too heavy with 0
struct timeval timeout;
timeout.tv_sec = TimeoutMs / 1000;
timeout.tv_usec = (TimeoutMs % 1000) * 1000;
return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && (FileDes < 0 || FD_ISSET(FileDes, &set));
}
bool cFile::FileReady(int FileDes, int TimeoutMs)
{
#ifdef DEBUG_OSD
refresh();
#endif
fd_set set;
struct timeval timeout;
FD_ZERO(&set);
FD_SET(FileDes, &set);
if (TimeoutMs < 100)
TimeoutMs = 100;
timeout.tv_sec = 0;
timeout.tv_usec = TimeoutMs * 1000;
return select(FD_SETSIZE, &set, NULL, NULL, &timeout) > 0 && FD_ISSET(FileDes, &set);
}
// --- cListObject -----------------------------------------------------------
cListObject::cListObject(void)
{
prev = next = NULL;
}
cListObject::~cListObject()
{
}
void cListObject::Append(cListObject *Object)
{
next = Object;
Object->prev = this;
}
void cListObject::Unlink(void)
{
if (next)
next->prev = prev;
if (prev)
prev->next = next;
next = prev = NULL;
}
int cListObject::Index(void)
{
cListObject *p = prev;
int i = 0;
while (p) {
i++;
p = p->prev;
}
return i;
}
// --- cListBase -------------------------------------------------------------
cListBase::cListBase(void)
{
objects = lastObject = NULL;
}
cListBase::~cListBase()
{
Clear();
while (objects) {
cListObject *object = objects->Next();
delete objects;
objects = object;
}
}
void cListBase::Add(cListObject *Object)
{
if (lastObject)
lastObject->Append(Object);
else
objects = Object;
lastObject = Object;
}
void cListBase::Del(cListObject *Object)
{
if (Object == objects)
objects = Object->Next();
if (Object == lastObject)
lastObject = Object->Prev();
Object->Unlink();
delete Object;
}
void cListBase::Move(int From, int To)
{
Move(Get(From), Get(To));
}
void cListBase::Move(cListObject *From, cListObject *To)
{
if (From && To) {
if (From->Index() < To->Index())
To = To->Next();
if (From == objects)
objects = From->Next();
if (From == lastObject)
lastObject = From->Prev();
From->Unlink();
if (To) {
if (To->Prev())
To->Prev()->Append(From);
From->Append(To);
}
else
lastObject->Append(From);
if (!From->Prev())
objects = From;
}
}
void cListBase::Clear(void)
{
while (objects) {
cListObject *object = objects->Next();
delete objects;
objects = object;
}
objects = lastObject = NULL;
}
cListObject *cListBase::Get(int Index) const
{
if (Index < 0)
return NULL;
cListObject *object = objects;
while (object && Index-- > 0)
object = object->Next();
return object;
}
int cListBase::Count(void) const
{
int n = 0;
cListObject *object = objects;
while (object) {
n++;
object = object->Next();
}
return n;
}