Subversion Repositories heizung

Rev

Rev 8 | Rev 10 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * (C) Copyright 2010, 2011 by Andreas Theofilu <andreas@theosys.at>
 * All rights reserved!
 */

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <libgen.h>
#include <math.h>
#include <time.h>
#include <signal.h>
#include <syslog.h>
#include <errno.h>
#include <pthread.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <dirent.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <pwd.h>
#include <grp.h>
#include "sensor.h"
#include "usb_comm.h"
#include "heizung.h"

typedef struct
{
      int wday;
      ulong start;
      ulong end;
      float temp;
}HEIZUNG;

typedef struct HEIZINDEX
{
      HEIZUNG *heizung;
      struct HEIZINDEX *next;
}HEIZINDEX;

struct SOCKETS
{
      int sockfd;
      int newfd;
};

CONFIGURE configs;
HEIZINDEX *HeizFirst;
float ActTemperature;
float ActPressure;

static pthread_t pthr_pars, pthr_temp_pars, pthr_process;

// Prototypes
void daemon_start(int ignsigcld);
void *pthr_temperature(void *pV_data);
void *pthr_parser(void *pV_data);
void sig_child(void);
void sendList(int s1);
int listSchemas(int s1);
int parseCommand(int s1, char *cmd);
void *processCommands(void *pV_data);
void changeToUser(char *, char *);

HEIZINDEX *allocateMemory(void);
HEIZINDEX *insertMemory(HEIZINDEX *pos);
void freeMemory(void);
HEIZINDEX *freeChainMember(HEIZINDEX *member);
void freeDay(int wday);
void insertMember(int wday, long endt, float temp);
char *makeFileName(char *ret, char *fname, int len);
int readHeizPlan(void);
int readHeizPlanName(char *fname);
int writeHeizPlan(void);
int writeHeizPlanName(char *fname);
void readConf(void);

char *readLine(int fd, char *buf, int bufLen);
char *trim(char *str);
char *remove_string(char *str, char *search, char *ret);

static pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t fastmutex_ser = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t fastmutex_proc = PTHREAD_MUTEX_INITIALIZER;

/*
 * The main program, initializing everything and start the daemon.
 */
int main(int argc, char *argv[])
{
int fd, index;

        HeizFirst = NULL;
        readConf();
        // Initialize USB
        serialDev.fd = -1;
        serialDev.switch_fd = -1;
        serialDev.handle = NULL;
        // Now daemonize this application
        daemon_start(0);
        changeToUser(&configs.User[0], &configs.Grp[0]);
        // Now start our Thread
        if (pthread_create(&pthr_pars, NULL, pthr_parser, (void *)0) != 0)
        {
           syslog (LOG_DAEMON,"Create of thread \"pthr_parser\" failed!");
           return 1;
        }

        // Here we start another thread to read the temperature
        if (pthread_create(&pthr_temp_pars, NULL, pthr_temperature, (void *)0) != 0)
        {
           syslog(LOG_DAEMON,"Create of thread \"pthr_temperature\" failed!");
           pthread_cancel(pthr_pars);
           return 1;
        }

        while (1)
           sleep(3600);

        pthread_exit(NULL);
        return 0;
}

/*
 * Detach application from console and make it a daemon.
 */
void daemon_start (int ignsigcld)
{
int childpid, fd;

        if (getpid () == 1)
           goto out;

#ifdef SIGTTOU
        signal (SIGTTOU, SIG_IGN);
#endif
#ifdef SIGTTIN
        signal (SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTSTP
        signal (SIGTSTP, SIG_IGN);
#endif

        if ((childpid = fork ()) < 0)
           fprintf (stderr, "Can't fork this child\n");
        else if (childpid > 0)
           exit (0);

        if (setpgrp () == -1)
           fprintf (stderr, "Can't change process group\n");

        signal (SIGHUP, SIG_IGN);

        if ((childpid = fork ()) < 0)
           syslog (LOG_DAEMON, "Can't fork second child");
        else if (childpid > 0)
           exit (0);            /* first child */

        /* second child */
out:
        for (fd = 0; fd < NOFILE; fd++)
           close (fd);

        errno = 0;
        chdir ("/");
        umask (0);

        if (ignsigcld)
        {
//#ifdef SIGTSTP
//           signal (SIGCLD, sig_child);
//#else
           signal (SIGCLD, SIG_IGN);
//#endif
        }
}

void sig_child ()
{
#if defined(BSD) && !defined(sinix) && !defined(Linux)
int pid;
union wait status;

        while ((pid = wait3 (&status, WNOHANG, (struct rusage *)0)) > 0)
                ;
#endif
}

void changeToUser(char *usr, char *grp)
{
gid_t gr_gid;

        if (usr && strlen(usr))
        {
           /* get uid */
           struct passwd *userpwd;
           struct group *usergrp;

           if ((userpwd = getpwnam(usr)) == NULL)
           {
              syslog(LOG_DAEMON,"no such user: %s", usr);
              exit(EXIT_FAILURE);
           }

           if (!grp || strlen(grp) || (usergrp = getgrnam(grp)) == NULL)
           {
              if (grp && strlen(grp))
                 syslog(LOG_WARNING,"no such group: %s", grp);

              gr_gid = userpwd->pw_gid;
           }
           else
              gr_gid = usergrp->gr_gid;

           if (setgid(gr_gid) == -1)
           {
              syslog(LOG_DAEMON,"cannot setgid of user %s: %s", usr, strerror(errno));
              exit(EXIT_FAILURE);
           }

#ifdef _BSD_SOURCE
           /* init suplementary groups
            * (must be done before we change our uid)
            */
           if (initgroups(usr, gr_gid) == -1)
              syslog(LOG_DAEMON,"cannot init suplementary groups of user %s: %s", usr, strerror(errno));
#endif

           /* set uid */
           if (setuid(userpwd->pw_uid) == -1)
           {
              syslog(LOG_DAEMON,"cannot change to uid of user %s: %s\n", usr, strerror(errno));
              exit(EXIT_FAILURE);
           }

           if(userpwd->pw_dir)
              setenv("HOME", userpwd->pw_dir, 1);
        }
}

void *pthr_temperature(void *pV_data)
{
time_t t, tstart;
struct tm *zeit, sz;
int sleep_sec;

        // Initialize the serial port.
        serial_set_method((strlen(configs.Device)) ? 1 : 0, configs.VID, configs.PID);

        if (strlen(configs.Device))
           serial_set_device(configs.Device);

        if (!serial_open())
           return NULL;

        if (serialDev.switch_fd == -1)
        {
           if (!serial_open())
              return NULL;
        }

        pthread_mutex_lock (&fastmutex_ser);
        sleep(1);       // Give the other thread time to initialize the structure

        while(1)
        {
           HEIZINDEX *act;

           GetTemp();
           sleep_sec = 300;

           // Compare the actual temperature with the one that should be
           // and switch heating on or off
           if (HeizFirst)
           {
              int wday;
              unsigned long loctm;

              t = time (NULL);
              sleep_sec = 300 - (int)((t + 300L) % 300L);
              sleep_sec++;
              zeit = localtime (&t);

              wday = (zeit->tm_wday == 0) ? 7 : zeit->tm_wday;
              memset (&sz, 0, sizeof (struct tm));
              sz.tm_min = 0;
              sz.tm_hour = 0;
              sz.tm_mon = zeit->tm_mon;
              sz.tm_mday = zeit->tm_mday;
              sz.tm_year = zeit->tm_year;
              sz.tm_isdst = zeit->tm_isdst;
              tstart = mktime(&sz);
              loctm = (t - tstart) + 1; // Seconds since midnight
              act = HeizFirst;

              while(act)
              {
                 if (act->heizung->wday == wday && loctm >= act->heizung->start && loctm <= act->heizung->end)
                 {
                    if (ActTemperature == 9999.0)
                       SwitchOff();             // No temperature, no heating
                    else if (ActTemperature < 5.0)
                       SwitchOn();              // Make sure it will not freeze
                    else if (ActTemperature > 30.0)
                       SwitchOff();             // Don't over heat
                    else if (ActTemperature <= (act->heizung->temp - 0.5))
                       SwitchOn();
                    else if (ActTemperature > act->heizung->temp)
                       SwitchOff();
                 }

                 act = act->next;
              }
           }
           else
              syslog(LOG_INFO,"Structure not initialized!");

           sleep(sleep_sec);            // Wait 5 Minutes
        }

        pthread_mutex_unlock(&fastmutex_ser);
}

void *processCommands(void *pV_data)
{
char ch, buf[128];
int i, s1, s;
struct SOCKETS *soc;

        soc = (struct SOCKETS *)pV_data;
        s1 = soc->newfd;
        s = soc->sockfd;
        memset(&buf[0], 0, sizeof(buf));
        i = 0;

        while (read(s1,&ch,1) > 0)
        {
           if (i < (sizeof(buf) - 1))
           {
              buf[i] = ch;

              if (ch == ';' || ch == 0x0d)
              {
              int pstat;

                 pthread_mutex_lock (&fastmutex_proc);

                 if (!strncmp(buf, "quit", 4))
                    break;

                 if ((pstat = parseCommand(s1,buf)) == 0)
                 {
                 char hv0[128];

                    sprintf(&hv0[0],"INVALID COMMAND: %s;",buf);
                    write(s1,hv0,strlen(hv0));
                 }
                 else if (pstat == 2)
                    write(s1, "NAK;", 4);
                 else
                    write(s1,"OK;",3);

                 memset(&buf[0], 0, sizeof(buf));
                 i = 0;
                 pthread_mutex_unlock(&fastmutex_proc);
                 continue;
              }
           }

           i++;
        }

        close(s1);
}

void *pthr_parser(void *pV_data)
{
char ch, str[INET_ADDRSTRLEN];
// socket structure
struct sockaddr_in client1, server1;
struct servent *serviceInfo;
int s1, s;
socklen_t length;
struct SOCKETS soc;

        // socket server
        if (configs.port > 0)
           server1.sin_port = htons(configs.port);
        else if ((serviceInfo = getservbyname ("heizung", "tcp")))
           server1.sin_port = serviceInfo->s_port;
        else
        {
           syslog(LOG_DAEMON,"Error: No TCP port defined!");
           exit(EXIT_FAILURE);
        }

        server1.sin_family = AF_INET;
        server1.sin_addr.s_addr = INADDR_ANY;
        s = socket(AF_INET,SOCK_STREAM,0);

        if (s < 0)
        {
           syslog (LOG_DAEMON, "Error in socket: %s",strerror(errno));
           exit(EXIT_FAILURE);
        }

        if (bind (s, (struct sockaddr *)&server1, sizeof (server1)) < 0)
        {
           syslog (LOG_DAEMON, "Error in bind: %s", strerror(errno));
           exit(EXIT_FAILURE);
        }

        if (listen (s, 5) < 0)
        {
           syslog (LOG_DAEMON, "Error in listen: %s", strerror(errno));
           exit(EXIT_FAILURE);
        }

        length = sizeof(client1);
        syslog (LOG_DEBUG, "Server ready: %d",s);

        while (1)
        {
           pthread_mutex_lock (&fastmutex);
           // Read time table at every loop, to be sure to have the latest
           // version (User may change it at every time)
           if (!readHeizPlan())
           {
              if (!HeizFirst)
              {
                 syslog(LOG_DAEMON, "Error reading table %s with plan!", configs.HeizPath);
                 exit(EXIT_FAILURE);
              }
              else
                 syslog(LOG_DAEMON, "Error reading table with plan! Will work with old one.");
           }

           pthread_mutex_unlock(&fastmutex);

           if ((s1 = accept (s, (struct sockaddr *)&client1, &length)) < 0)
           {
              syslog (LOG_DAEMON, "Error in accept: %d: %s", s1, strerror(errno));
              continue;
           }

           inet_ntop(AF_INET, &(client1.sin_addr), str, INET_ADDRSTRLEN);
           syslog (LOG_INFO, "Connected to client %s", str);
           soc.sockfd = s;
           soc.newfd = s1;

           if (pthread_create(&pthr_process, NULL, processCommands, (void *)&soc) != 0)
           {
              syslog (LOG_DAEMON,"Create of thread \"pthr_parser\" failed!");
              close(s1);
           }
        }

        close (s);
        serial_close();
        return NULL;
}

void sendList(int s1)
{
char hv0[256];
int i, wday = 1;
HEIZINDEX *act;

        // We will write out the week days in the correct order.
        // So we need 2 loops. The first one is runnung for every week day
        // and the 2nd one will find every entry for the actual week day.
        while (wday <= 7)
        {
           act = HeizFirst;
           i = 1;               // The line counter --> reset it to 1 for every week day

           while (act)
           {
              if (act->heizung->wday == wday)
              {
                 sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%.1f;", i, act->heizung->wday,
                        act->heizung->start, act->heizung->end, act->heizung->temp);
                 write(s1, &hv0[0], strlen(hv0));
                 i++;
              }

              act = act->next;
           }

           wday++;
        }
}

int listSchemas(int s1)
{
char hv0[256];
DIR *dir;       
struct dirent *de;
char *ldup, *lp;

        if ((ldup = strdup(configs.HeizPath)) == NULL)
        {
           syslog(LOG_DAEMON, "Not enough memory for path: %s", strerror(errno));
           return 2;
        }

        lp = dirname(ldup);

        if ((dir = opendir(lp)) == NULL)
        {
           syslog(LOG_DAEMON, "Could not open directory %s: %s", lp, strerror(errno));
           free(ldup);
           return 2;
        }

        strcpy(&hv0[0], "SCHEMA START;");
        write(s1, hv0, strlen(hv0));

        while ((de = readdir(dir)) != NULL)
        {
           if (de->d_type == DT_REG && strstr(de->d_name, ".heat") != NULL)
           {
           char dn[256], *p;

              memset(&dn[0], 0, sizeof(dn));
              strncpy(dn, de->d_name, sizeof(dn)-1);

              if ((p = strrchr(dn, '.')) != NULL)
                 *p = 0;
              else
                 continue;

              sprintf(&hv0[0], "SCHEMA FILE:%s;", dn);
              write(s1, hv0, strlen(hv0));
           }
        }

        closedir(dir);
        free(ldup);
        return 1;
}

int parseCommand(int s1, char *cmd)
{
char bef[32],par[1024], *p;
char hv0[256];
int i,j;
HEIZINDEX *act, *last;

        memset(bef,0,sizeof(bef));
        memset(par,0,sizeof(par));

        if ((p = strchr(cmd,':')) != NULL)              // do we have a parameter?
        {
           strncpy(bef,cmd,p - cmd);
           strncpy(par,p+1,strlen(p)-2);        // Cut off the trailing semi colon
        }
        else
           strncpy(bef,cmd,strlen(cmd)-1);

        if (!strcasecmp(bef, "LIST"))           // Write out current list
           sendList(s1);

        if (!strcasecmp(bef, "GET WDAY"))               // Write out a particular week day
        {
        int wday = atoi(par);

           if (wday < 1 || wday > 7)
           {
              strcpy(&hv0[0], "ERROR:Invalid week day");
              write(s1, &hv0[0], strlen(hv0));
              return 0;
           }

           act = HeizFirst;

           while (act && act->heizung->wday != wday)
              act = act->next;

           if (!act)
           {
              sprintf(&hv0[0], "ERROR:No plan for week day %d", wday);
              write(s1, &hv0[0], strlen(hv0));
              return 0;
           }

           i = 1;

           while (act && act->heizung->wday == wday)
           {
              sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%.1f;", i, act->heizung->wday,
                      act->heizung->start, act->heizung->end, act->heizung->temp);
              write(s1, &hv0[0], strlen(hv0));
              i++;
              act = act->next;
           }
        }

        if (!strcasecmp(bef, "GET TEMP"))       // Return actual temperature
        {
           sprintf(&hv0[0], "TEMP:%.1f;", ActTemperature);
           write(s1, &hv0[0], strlen(hv0));
        }

        if (!strcasecmp(bef, "GET PRESSURE"))   // Return the actual air pressure
        {
           sprintf(&hv0[0], "PRESSURE:%.1f;", ActPressure);
           write(s1, &hv0[0], strlen(hv0));
        }

        if (!strcasecmp(bef, "HEATSTAT"))       // Return the status of the heating
        {
           sprintf(&hv0[0], "HEATSTAT:%d;", HeatStatus);
           write(s1, &hv0[0], strlen(hv0));
        }

        // SET DAY:<count>:<day>:<end1>:<temp>[:<end2>:<temp>[:...]];
        // <count>   number of entries following
        // <day>     The day of the week
        // <end1>    The end time
        // <temp>    The temperature wanted until end time is reached
        //
        if (!strcasecmp(bef, "SET DAY"))                // Set the plan for a particular day
        {
        int count, wday, i;
        long endt;
        float temp;

           count = atoi(par);
           remove_string(par, ":", &hv0[0]);
           wday = atoi(par);
           freeDay(wday);
           last = NULL;

           if (count > 0)
           {
              for (i = 0; i < count; i++)
              {
                 remove_string(par, ":", &hv0[0]);
                 endt = atol(par);
                 remove_string(par, ":", &hv0[0]);
                 temp = atof(par);

                 if ((act = allocateMemory()) == NULL)
                 {
                    syslog(LOG_DAEMON,"Error allocating memory for a temperature line.");
                    sprintf(&hv0[0], "ERROR:Not enough memory!");
                    write(s1, &hv0[0], strlen(hv0));
                    return 2;
                 }

                 act->heizung->wday = wday;
                 act->heizung->end = endt;
                 act->heizung->temp = temp;

                 if (last)
                    act->heizung->start = last->heizung->end;
                 else
                    act->heizung->start = 0L;

                 last = act;
              }

              writeHeizPlan();
           }
        }

        // SET TEMP:<wday>:<end>:<temp>;
        if (!strcasecmp(bef, "SET TEMP"))       // Set the temperature for a particular day and line
        {
        int wday;
        unsigned long endt;
        float tmp;

           wday = atoi(par);
           remove_string(par, ":", &hv0[0]);
           endt = atol(par);
           remove_string(par, ":", &hv0[0]);
           tmp = atof(par);
           insertMember(wday, endt, tmp);
           writeHeizPlan();
        }

        // SAVE SCHEMA:<name>;
        if (!strcasecmp(bef, "SAVE SCHEMA"))    // Save the current heating plan into a file
        {
           sprintf(&hv0[0], "%s.heat", par);

           if (!writeHeizPlanName(hv0))
              return 2;
        }

        // LOAD SCHEMA:<name>;
        if (!strcasecmp(bef, "LOAD SCHEMA"))    // Load a previous saved schema from a file
        {
           sprintf(&hv0[0], "%s.heat", par);

           if (!readHeizPlanName(hv0))
              return 2;

           // write the new schema immediately to the default file
           writeHeizPlan();
           sendList(s1);
        }

        // DELETE SCHEMA:<name>;
        if (!strcasecmp(bef, "DELETE SCHEMA"))  // Delete an existing schema
        {
           makeFileName(&hv0[0], par, sizeof(hv0));
           
           if (strlen(hv0) < (sizeof(hv0) - 6))
              strcat(&hv0[0], ".heat");
           else
           {
              syslog(LOG_DAEMON, "File name too large for internal buffer");
              return 2;
           }

           if (access(hv0, R_OK | W_OK))
           {
              syslog(LOG_DAEMON,"No access to file %s: %s", hv0, strerror(errno));
              return 2;
           }

           if (unlink(hv0) == -1)
           {
              syslog(LOG_DAEMON,"Error deleting file %s: %s", hv0, strerror(errno));
              return 2;
           }

           return listSchemas(s1);
        }

        // LIST SCHEMA;
        if (!strcasecmp(bef, "LIST SCHEMA"))    // Return a list of all available schamas
           return listSchemas(s1);

        return 1;
}

/*
 * Remove a complete day
 */
void freeDay(int wday)
{
HEIZINDEX *act, *next, *prev;

        act = HeizFirst;
        prev = NULL;

        while(act)
        {
           if (act->heizung->wday == wday)
              act = freeChainMember(act);

           act = act->next;
        }  
}

/*
 * Insert a new entry
 */
void insertMember(int wday, long endt, float temp)
{
HEIZINDEX *act;

        act = HeizFirst;

        while(act)
        {
           if (act->heizung->wday == wday && act->heizung->end == endt)
           {
              act->heizung->temp = temp;
              return;
           }

           act = act->next;
        }

        if ((act = allocateMemory()) != NULL)
        {
           act->heizung->wday = wday;
           act->heizung->end = endt;
           act->heizung->temp = temp;
        }
}

/*
 * Free the memory for the actual heizung plan.
 */
void freeMemory()
{
HEIZINDEX *act, *next;

        act = HeizFirst;

        while(act)
        {
           if (act->heizung)
           {
              free(act->heizung);
              act->heizung = NULL;
           }

           next = act->next;
           free(act);
           act = next;
        }

        HeizFirst = NULL;
}

/*
 * Free only one entry in the chain
 */
HEIZINDEX *freeChainMember(HEIZINDEX *member)
{
HEIZINDEX *act,*prev;

        act = HeizFirst;
        prev = NULL;

        while(act != member)
        {
           prev = act;
           act = act->next;
        }

        if (act == member)
        {
           if (prev)
              prev->next = act->next;
           else
           {
              prev = act->next;

              if (act == HeizFirst)
                 HeizFirst = act->next;
           }

           if (act->heizung)
              free(act->heizung);

           free(act);
           return prev;
        }

        return NULL;
}

/*
 * Allocate the memory for the actual heizung plan.
 * This function appends an element to the end of the chain.
 */
HEIZINDEX *allocateMemory()
{
HEIZINDEX *act, *last;

        if (!HeizFirst)
        {
           HeizFirst = malloc(sizeof(HEIZINDEX));

           if (HeizFirst)
           {
              HeizFirst->heizung = malloc(sizeof(HEIZUNG));
              HeizFirst->next = NULL;
           }
           else
              return NULL;

           return HeizFirst;
        }
        else
        {
           // Find last element
           last = HeizFirst;

           while(last->next)
              last = last->next;

           act = malloc(sizeof(HEIZINDEX));

           if (act)
           {
              act->heizung = malloc(sizeof(HEIZUNG));
              act->next = NULL;
              last->next = act;
           }
           else
              return NULL;

           return act;
        }

        return NULL;
}

/*
 * Allocate the memory for the actual heizung plan.
 * This function inserts an element into the chain.
 */
HEIZINDEX *insertMemory(HEIZINDEX *pos)
{
HEIZINDEX *act;

        if (!HeizFirst)
        {
           HeizFirst = malloc(sizeof(HEIZINDEX));

           if (HeizFirst)
           {
              HeizFirst->heizung = malloc(sizeof(HEIZUNG));
              HeizFirst->next = NULL;
           }
           else
              return NULL;

           return HeizFirst;
        }
        else
        {
           act = malloc(sizeof(HEIZINDEX));

           if (act)
           {
              act->heizung = malloc(sizeof(HEIZUNG));
              act->next = pos->next;
              pos->next = act;
           }
           else
              return NULL;

           return act;
        }

        return NULL;
}

/*
 * The following functions read a config file and put the
 * contents into a structure.
 */
void readConf(void)
{
int fd;
char confFile[512], line[512];
char *home, *p;
char hv0[64], hv1[128];

        home = getenv("HOME");
        fd = -1;

        if (!access("/etc/heizung.conf",R_OK))
           strcpy(confFile,"/etc/heizung.conf");
        else if (!access("/etc/heizung/heizung.conf",R_OK))
           strcpy(confFile,"/etc/heizung/heizung.conf");
        else if (!access("/usr/etc/heizung.conf",R_OK))
           strcpy(confFile,"/usr/etc/heizung.conf");
        else if (home)
        {
           strcpy(confFile,home);
           strcat(confFile,"/.heizung.conf");

           if (access(confFile,R_OK))
           {
              syslog(LOG_WARNING,"Even config file %s was not found!", confFile);
              confFile[0] = 0;
           }
        }
        else
           confFile[0] = 0;

        memset(&configs, 0, sizeof(configs));
        strcpy(configs.User,"nobody");
        strcpy(configs.Grp,"nobody");
        strcpy(configs.HeizPath, "/var/www/.HeizPlan.conf");
        strcpy(configs.Werte, "/var/log/werte.dat");
        strcpy(configs.Device, "/dev/ttyS1");
        configs.port = 11001;

        if (!confFile[0] || (fd = open(confFile,O_RDONLY)) == -1)
        {
           if (confFile[0])
              syslog(LOG_WARNING,"Error opening the config file %s! Using built in defaults. (%s)", confFile, strerror(errno));
           else
              syslog(LOG_WARNING,"Error opening the config file! Using built in defaults.");

           return;
        }

        while (readLine(fd, &line[0], sizeof(line)) != NULL)
        {
           int len;

           trim (&line[0]);

           if (line[0] == '#' || !strlen(line))
              continue;

           if ((p = strchr (line, '=')) == NULL)
              continue;

           *p = 0;
           p++;
           len = strlen(line);

           if (len > sizeof(hv0))
              len = sizeof(hv0) - 1;

           strncpy (hv0, line, len);
           hv0[len] = 0;
           trim (hv0);
           len = strlen(p);

           if (len > sizeof(hv1))
              len = sizeof(hv0) - 1;

           strncpy (hv1, p, len);
           hv1[len] = 0;
           trim (hv1);

           if (!strcasecmp(hv0, "user"))
           {
              syslog(LOG_INFO,"Found \"user\": %s", hv1);
              strncpy (configs.User, hv1, sizeof(configs.User));
           }

           if (!strcasecmp(hv0, "group"))
           {
              syslog(LOG_INFO,"Found \"group\": %s", hv1);
              strncpy (configs.Grp, hv1, sizeof(configs.Grp));
           }

           if (!strcasecmp(hv0, "port"))
           {
              syslog(LOG_INFO,"Found \"port\": %s", hv1);
              configs.port = atoi (hv1);
           }

           if (!strcasecmp(hv0, "heizpath"))
           {
              syslog(LOG_INFO,"Found \"heizpath\": %s", hv1);
              strncpy (configs.HeizPath, hv1, sizeof(configs.HeizPath));
           }

           if (!strcasecmp(hv0, "Werte"))
           {
              syslog(LOG_INFO,"Found \"Werte\": %s", hv1);
              strncpy (configs.Werte, hv1, sizeof(configs.Werte));
           }

           if (!strcasecmp(hv0, "Device"))
           {
              syslog(LOG_INFO,"Found \"Device\": %s", hv1);
              strncpy (configs.Device, hv1, sizeof(configs.Device));
           }

           if (!strcasecmp(hv0, "VID"))
           {
              syslog(LOG_INFO,"Found VendorID: %04x", atoi(hv1));
              configs.VID = atoi(hv1);
           }

           if (!strcasecmp(hv0, "PID"))
           {
              syslog(LOG_INFO,"Found ProductID: %04x", atoi(hv1));
              configs.PID = atoi(hv1);
           }
        }

        close (fd);
}

char *makeFileName(char *ret, char *fname, int len)
{
char *fdup, *pright, *cfg, *lcfg;

        if (ret == NULL || fname == NULL || !len || !strlen(fname))
           return NULL;

        if (!access(fname, R_OK | W_OK))
        {
           memset(ret, 0, len);
           strncpy(ret, fname, len-1);
           return ret;
        }

        fdup = strdup(fname);

        if (fdup == NULL)
        {
           syslog(LOG_DAEMON, "Error allocating memory: %s", strerror(errno));
           return NULL;
        }

        cfg = strdup(configs.HeizPath);
        
        if (cfg == NULL)
        {
           syslog(LOG_DAEMON, "Error allocating memory: %s", strerror(errno));
           free(fdup);
           return NULL;
        }

        pright = basename(fdup);
        lcfg = dirname(cfg);
        memset(ret, 0, len);
        strncpy(ret, lcfg, len-1);
        
        if (strlen(ret) < (len - (strlen(pright) + 2)))
        {
           strcat(ret, "/");
           strcat(ret, pright);
           free(fdup);
           free(cfg);
           return ret;
        }
        
        free(fdup);
        free(cfg);
        return NULL;
}

int readHeizPlan(void)
{
        return readHeizPlanName(configs.HeizPath);
}

int readHeizPlanName(char* fname)
{
int fd, i, wday;
ulong tim;
char line[512];
char *p, *xp;
char hv0[64], hv1[128];
char path[512];
float temperature;
int counter = 0;
HEIZINDEX *act, *prev;

        fd = -1;

        if (makeFileName(&path[0], fname, sizeof(path)) == NULL)
        {
           syslog(LOG_DAEMON,"No or invalid path %s", fname);
           return 0;
        }

        if (access(path, R_OK))
        {
           syslog(LOG_DAEMON,"Access to file %s denied: %s", path, strerror(errno));
           return 0;
        }

        if ((fd = open(path, O_RDONLY)) == -1)
        {
           syslog(LOG_DAEMON,"Error opening file %s: %s", path, strerror(errno));
           return 0;
        }

        act = prev = NULL;
        freeMemory();

        while (readLine(fd, &line[0], sizeof(line)) != NULL)
        {
           int len;

           trim (&line[0]);

           if (line[0] == '#' || !strlen(line) || strchr(line, ',') == NULL)
              continue;

           // We need a place to store the information
           prev = act;

           if ((act = allocateMemory()) == NULL)
           {
              close(fd);
              syslog(LOG_DAEMON,"Error allocating memory for a temperature line! Stopped reading file %s.", configs.HeizPath);
              return 0;
           }

           counter++;
           memset(act->heizung, 0, sizeof(HEIZUNG));
           // Parse a line. The line has the format:
           // <wday>,<end>,<temperature>
           p = strtok(line, ",");
           i = 1;

           while(p)
           {
              switch(i)
              {
                 case 1:        // Week day
                    wday = atoi(p);

                    if (wday < 1 || wday > 7)   // valid line?
                    {
                       if (prev)
                          wday = prev->heizung->wday;

                       if (wday < 1 || wday > 7)
                       {
                          p = strtok(NULL, ",");
                          i++;
                          continue;
                       }
                    }

                    act->heizung->wday = wday;
                    act->heizung->start = 0;
                 break;

                 case 2:        // start/end time
                    if ((xp = strchr(p, ':')) != NULL)
                    {
                    int hour, min;

                       hour = atoi(p);
                       min = atoi(xp+1);

                       if (hour >= 0 && hour <= 23 && min >= 0 && min <= 59)
                       {
                          act->heizung->end = hour * 3600 + min * 60 + 59;

                          if (prev && prev->heizung->wday == act->heizung->wday)
                             act->heizung->start = prev->heizung->end;
                       }
                    }
                 break;

                 case 3:        // temperature
                    temperature = atof(p);

                    if (temperature < 5.0 || temperature > 30.0)
                    {
                       p = strtok(NULL,",");
                       i++;
                       continue;
                    }

                    act->heizung->temp = temperature;
                 break;
              }

              p = strtok(NULL, ",");
              i++;
           }
        }

        syslog(LOG_INFO,"Found %d entries in %s", counter, configs.HeizPath);
        close (fd);
        return 1;
}

/*
 * write the (may be) altered plan.
 * This function allways writes whole plan.
 */
int writeHeizPlan()
{
        return writeHeizPlanName(configs.HeizPath);
}

int writeHeizPlanName(char *fname)
{
int fd;
char hv0[512], path[512];
HEIZINDEX *act;

        fd = -1;

        if (makeFileName(&path[0], fname, sizeof(path)) == NULL)
        {
           syslog(LOG_DAEMON,"No or invalid path %s", fname);
           return 0;
        }

        if ((fd = open(path, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
        {
           syslog(LOG_DAEMON, "Error opening/creating file %s: %s", path, strerror(errno));
           return 0;
        }

        act = HeizFirst;

        while(act)
        {
           if (act->heizung->wday)
           {
              int hour, min;

              hour = act->heizung->end / 3600;
              min = (act->heizung->end - (hour * 3600)) / 60;

              if (hour > 23)
              {
                 hour = 23;
                 min = 59;
              }

              sprintf(&hv0[0], "%d,%02d:%02d,%.1f\n",
                   act->heizung->wday, hour, min,
                   act->heizung->temp);
              write(fd, &hv0[0], strlen(hv0));
           }

           act = act->next;
        }

        close(fd);
        return 1;
}

char *readLine(int fd, char *buf, int bufLen)
{
int i, end;
char ch, *p;

        if (fd <= 0)
        {
           syslog(LOG_DAEMON,"Function readLine was called with an invalid file descriptor of %d!", fd);
           return NULL;
        }

        i = end = 0;
        p = buf;

        while (read(fd, &ch, 1) > 0)
        {
           end = 1;

           if (ch == 0x0a)
           {
             *p = 0;
             return buf;
           }

           if (ch == 0x0d)      // ignore this!
              continue;

           if (i < (bufLen - 1))
           {
              *p = ch;
              p++;
              i++;
           }
        }

        *p = 0;

        if (end)
           return buf;
        else
           return NULL;
}

char *trim(char *str)
{
char *p1, *p2, *p;

        if (!str)
           return NULL;

        if (!strlen(str))
           return str;

        p = str;
        p1 = p2 = NULL;

        while (*p)
        {
           if (!p1 && *p != ' ')
           {
              p1 = p;
              break;
           }

           p++;
        }

        p2 = str + (strlen(str) - 1);

        while (p2 > str && *p2 == ' ')
           p2--;

        if (p2)
           *(p2+1) = 0;

        if (p1)
        {
           char *buf = strdup (p1);
           strcpy (str, buf);
           free (buf);
        }

        return str;
}

char *remove_string(char *str, char *search, char *ret)
{
char *p;

        if (!strlen(str) || !strlen(search))
           return NULL;

        if ((p = strstr(str, search)) != NULL)
        {
        int len = strlen(search);

           strncpy(ret, str, p - str + len);
           ret[p - str + len] = 0;
           memmove(str, p + len, strlen(p+len));
           str[strlen(p+len)] = 0;
           return ret;
        }

        return NULL;
}