Rev 3 | Rev 5 | 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;
struct HEIZINDEX *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);
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);
int readHeizPlan(void);
int writeHeizPlan(void);
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;
/*
* 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[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;
}
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 == ';' || ch == 0x0d)
{
if (!strncmp(buf, "quit", 4))
break;
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[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+1));
}
else
strncpy(bef,cmd,strlen(cmd)-1);
sprintf(&hv0[0], "%s --> %s\n", bef, par);
write(s1, hv0, strlen(hv0));
if (!strcasecmp(bef, "LIST")) // Write out current list
{
act = HeizFirst;
i = 1;
while (act)
{
sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%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(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:%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;
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 0;
}
act->heizung->wday = wday;
act->heizung->end = endt;
act->heizung->temp = temp;
if (last)
act->heizung->start = act->heizung->end;
else
act->heizung->start = 0L;
last = act;
}
}
}
// 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);
}
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;
}
}
/*
* 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");
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));
}
}
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, *prev;
fd = -1;
if (access(configs.HeizPath, R_OK))
{
syslog(LOG_DAEMON,"Access to file %s denied: %s", configs.HeizPath, strerror(errno));
return 0;
}
if ((fd = open(configs.HeizPath, O_RDONLY)) == -1)
{
syslog(LOG_DAEMON,"Error opening file %s: %s", configs.HeizPath, strerror(errno));
return 0;
}
act = prev = NULL;
freeMemory();
while (readLine(fd, &line[0], sizeof(line)) != NULL)
{
int len;
trim (&line[0]);
if (line[0] == '#')
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;
}
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 + 1) * 60;
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++;
}
}
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,%lu,%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, 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);
memmove(str, p + len + 1, strlen(p+len));
return ret;
}
return NULL;
}