Subversion Repositories heizung

Rev

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

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

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.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 <netinet/in.h>
#include <sys/param.h>
#include <sys/ioctl.h>
#include <pwd.h>
#include <grp.h>

typedef struct
{
      char User[32];
      char Grp[32];
      char HeizPath[256];
      int port;
}CONFIGURE;

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

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

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

static pthread_t pthr_pars;

// Prototypes
void daemon_start(int ignsigcld);
void *pthr_parser(void *pV_data);
void sig_child(void);
int parseCommand(int s1, char *cmd);

HEIZINDEX *allocateMemory(void);
void freeMemory(void);
void freeChainMember(HEIZINDEX *member);
int readHeizPlan(void);
int writeHeizPlan(void);
void readConf(void);

char *readLine(int fd, long *offset, 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;

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

        HeizFirst = NULL;
        readConf();
        // Now daemonize this application
        daemon_start(0);
        changeToUser(configs.User, configs.Grp);
        // 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;
        }

        while (1)
           sleep(3600);

        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_parser( void *pV_data )
{
int i;
char ch, buf[128];
// socket structure
struct sockaddr_in client1, server1;
struct servent *serviceInfo;
int s1,s;
socklen_t length;

        // 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");

        if (bind (s, (struct sockaddr *)&server1, sizeof (server1)) < 0)
           syslog (LOG_DAEMON, "Error in bind");

        if (listen (s, 5) < 0)
           syslog (LOG_DAEMON, "Error in listen");

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

        while (1)
        {
           // 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 with plan!");
                 return NULL;
              }
              else
                 syslog(LOG_DAEMON, "Error reading table with plan! Will work with old one.");
           }

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

           pthread_mutex_lock (&fastmutex);
           syslog (LOG_INFO, "Connected to client");
           memset(&buf[0], 0, sizeof(buf));
           i = 0;

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

                 if (ch == ';')
                 {
//                  buf[i+1] = 0;

                    if (!parseCommand(s1,buf))
                    {
                    char hv0[128];

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

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

              i++;
           }

           pthread_mutex_unlock(&fastmutex);
           close (s1);
        }

        return NULL;
}

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

        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) - 1);
           strncpy(par,p,strlen(p)-1);
        }
        else
           strncpy(bef,cmd,strlen(cmd)-1);

        if (strcasecmp(bef, "LIST"))            // Write out current list
        {
           act = HeizFirst;
           i = 1;

           while (act)
           {
              sprintf(&hv0[0], "LINE:%d:%d:%d:%d:%f;", 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 WDAY"))                // Write out a particular week day
        {
        int wday = atoi(cmd);

           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:%d:%d:%f;", 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:%f;", ActTemperature);
           write(s1, &hv0[0], strlen(hv0));
        }

        if (strcasecmp(bef, "GET PRESSURE"))    // Return the actual air pressure
        {
           sprintf(&hv0[0], "PRESSURE:%f;", ActPressure);
           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;

           remove_string(bef, "SET DAY:", &hv0[0]);
           count = atoi(bef);

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

                 act = HeizFirst;

                 while(act)
                 {
                    if (act->heizung->wday == wday)
              }
           }
        }

        if (strcasecmp(bef, "SET PLAN"))        // Set the complete plan
        {
        }

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

           if ((p = strchr(cmd, ':')) != NULL)
           {
              wday = atoi(cmd);
              line = atoi(p+1);

              if (wday > 0 && wday <= 7 && line > 0 && (p = strchr(p+1, ':')) != NULL)
              {
                 tmp = atof(p+1);

                 if (tmp < 5.0)
                    tmp = 5.0;

                 if (tmp > 30.0)
                    tmp = 30.0;

                 act = HeizFirst;
                 i = 1;

                 while(act)
                 {
                    if (act->heizung->wday == wday)
                    {
                       if (i == line)
                       {
                          act->heizung->temp = tmp;
                          writeHeizPlan();
                       }

                       i++;
                    }

                    act = act->next;
                 }
              }
           }
        }

        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);
              continue;
           }

           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;
        }
}

/*
 * 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;

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

           free(act);
           return prev->next;
        }

        return NULL;
}

/*
 * Allocate the memory for the actual heizung plan,
 */
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;
}

/*
 * 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];
long offset = 0;

        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))
              confFile[0] = 0;
        }
        else
           confFile[0] = 0;

        memset(&configs, 0, sizeof(configs));

        if (!confFile[0] && (fd = open(confFile,O_RDONLY)) == -1)
        {
           syslog(LOG_WARNING,"Error opening the config file! Using built in defaults.");
           strcpy(configs.User,"nobody");
           strcpy(configs.Grp,"nobody");
           strcpy(configs.HeizPath, "/var/www/.HeizPlan.conf");
           configs.port = 11001;
           return;
        }

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

           trim (&line[0]);

           if (line[0] == '#')
              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);
           trim (hv0);
           len = strlen(p);

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

           strncpy (hv1, p, len);
           trim (hv1);

           if (!strcasecmp(hv0, "user"))
              strncpy (configs.User, hv1, sizeof(configs.User));

           if (!strcasecmp(hv0, "group"))
              strncpy (configs.Grp, hv1, sizeof(configs.Grp));

           if (!strcasecmp(hv0, "port"))
              configs.port = atoi (hv1);

           if (!strcasecmp(hv0, "heizpath"))
              strncpy (configs.HeizPath, hv1, sizeof(configs.HeizPath));
        }

        close (fd);
}

int readHeizPlan(void)
{
int fd, i, wday;
ulong tim;
char line[512];
char *p, *xp;
char hv0[64], hv1[128];
float temperature;
long offset = 0;
HEIZINDEX *act;

        fd = -1;

        if (!access(configs.HeizPath, R_OK))
           return 0;

        if ((fd = open(configs.HeizPath, O_RDONLY)) == -1)
           return 0;

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

           trim (&line[0]);

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

           // 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?
                    {
                       p = strtok(NULL, ',');
                       continue;
                    }

                    // We need a place to store the information
                    if ((act = allocateMemory()) == NULL)
                    {
                       close(fd);
                       return 0;
                    }

                    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;

                          if (act->next)
                             act->next->start = hour * 3600 + min;
                       }
                    }
                 break;

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

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

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

              p = strtok(NULL, ',');
           }
        }

        close (fd);
        return 1;
}

/*
 * write the (may be) altered plan.
 * This function allways writes whole plan.
 */
int writeHeizPlan()
{
int fd;
char hv0[512];
HEIZINDEX *act;

        fd = -1;

        if (!access(configs.HeizPath, R_OK))
           return 0;

        if ((fd = open(configs.HeizPath, O_RDWR | O_TRUNC)) == -1)
           return 0;

        act = HeizFirst;

        while(act)
        {
           sprintf(&hv0[0], "%d,%d,%f\n",
                   act->heizung->wday, act->heizung->end,
                   act->heizung->temp);
           write(fd, &hv0[0], strlen(hv0));
           act = act->next;
        }

        close(fd);
        return 1;
}

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

        if (fd <= 0)
           return NULL;

        lseek(fd, *offset, SEEK_SET);
        i = 0;
        p = buf;

        while (read(fd, &ch, 1) > 0)
        {
           if (ch == 0x0a)
           {
             *p = 0;
             *offset++;
             return buf;
           }

           if (ch == 0x0d)      // ignore this!
           {
              *offset++;
              continue;
           }

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

           *offset++;
        }

        *p = 0;
        return buf;
}

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

        if (!str)
           return NULL;

        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);

           strcpy(ret, str, p - str + len);
           memmove(str, p + len + 1, strlen(p+len));
           return ret;
        }

        return NULL;
}

Generated by GNU Enscript 1.6.5.90.