Subversion Repositories heizung

Rev

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

Rev Author Line No. Line
1 root 1
/*
5 andreas 2
 * (C) Copyright 2010, 2011 by Andreas Theofilu <andreas@theosys.at>
1 root 3
 * All rights reserved!
4
 */
5
 
6
#include <stdio.h>
7
#include <string.h>
8
#include <unistd.h>
9
#include <stdlib.h>
9 andreas 10
#include <libgen.h>
1 root 11
#include <math.h>
12
#include <time.h>
13
#include <signal.h>
14
#include <syslog.h>
15
#include <errno.h>
16
#include <pthread.h>
17
#include <sys/stat.h>
18
#include <sys/types.h>
19
#include <sys/socket.h>
20
#include <fcntl.h>
21
#include <netdb.h>
9 andreas 22
#include <dirent.h>
1 root 23
#include <netinet/in.h>
24
#include <sys/param.h>
25
#include <sys/ioctl.h>
26
#include <pwd.h>
27
#include <grp.h>
5 andreas 28
#include "sensor.h"
29
#include "usb_comm.h"
30
#include "heizung.h"
1 root 31
 
32
typedef struct
33
{
34
      int wday;
35
      ulong start;
36
      ulong end;
37
      float temp;
38
}HEIZUNG;
39
 
40
typedef struct HEIZINDEX
41
{
42
      HEIZUNG *heizung;
4 andreas 43
      struct HEIZINDEX *next;
1 root 44
}HEIZINDEX;
45
 
9 andreas 46
struct SOCKETS
47
{
48
      int sockfd;
49
      int newfd;
50
};
51
 
1 root 52
CONFIGURE configs;
53
HEIZINDEX *HeizFirst;
54
float ActTemperature;
55
float ActPressure;
56
 
9 andreas 57
static pthread_t pthr_pars, pthr_temp_pars, pthr_process;
1 root 58
 
59
// Prototypes
60
void daemon_start(int ignsigcld);
5 andreas 61
void *pthr_temperature(void *pV_data);
1 root 62
void *pthr_parser(void *pV_data);
63
void sig_child(void);
9 andreas 64
void sendList(int s1);
65
int listSchemas(int s1);
1 root 66
int parseCommand(int s1, char *cmd);
9 andreas 67
void *processCommands(void *pV_data);
4 andreas 68
void changeToUser(char *, char *);
1 root 69
 
70
HEIZINDEX *allocateMemory(void);
4 andreas 71
HEIZINDEX *insertMemory(HEIZINDEX *pos);
1 root 72
void freeMemory(void);
4 andreas 73
HEIZINDEX *freeChainMember(HEIZINDEX *member);
74
void freeDay(int wday);
75
void insertMember(int wday, long endt, float temp);
9 andreas 76
char *makeFileName(char *ret, char *fname, int len);
1 root 77
int readHeizPlan(void);
9 andreas 78
int readHeizPlanName(char *fname);
1 root 79
int writeHeizPlan(void);
9 andreas 80
int writeHeizPlanName(char *fname);
1 root 81
void readConf(void);
82
 
4 andreas 83
char *readLine(int fd, char *buf, int bufLen);
1 root 84
char *trim(char *str);
2 andreas 85
char *remove_string(char *str, char *search, char *ret);
1 root 86
 
87
static pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
5 andreas 88
static pthread_mutex_t fastmutex_ser = PTHREAD_MUTEX_INITIALIZER;
9 andreas 89
static pthread_mutex_t fastmutex_proc = PTHREAD_MUTEX_INITIALIZER;
1 root 90
 
91
/*
92
 * The main program, initializing everything and start the daemon.
93
 */
94
int main(int argc, char *argv[])
95
{
96
int fd, index;
97
 
98
	HeizFirst = NULL;
99
	readConf();
5 andreas 100
	// Initialize USB
101
	serialDev.fd = -1;
102
	serialDev.switch_fd = -1;
103
	serialDev.handle = NULL;
1 root 104
	// Now daemonize this application
105
	daemon_start(0);
4 andreas 106
	changeToUser(&configs.User[0], &configs.Grp[0]);
1 root 107
	// Now start our Thread
108
	if (pthread_create(&pthr_pars, NULL, pthr_parser, (void *)0) != 0)
109
	{
110
	   syslog (LOG_DAEMON,"Create of thread \"pthr_parser\" failed!");
111
	   return 1;
112
	}
113
 
5 andreas 114
	// Here we start another thread to read the temperature
115
	if (pthread_create(&pthr_temp_pars, NULL, pthr_temperature, (void *)0) != 0)
116
	{
117
	   syslog(LOG_DAEMON,"Create of thread \"pthr_temperature\" failed!");
118
	   pthread_cancel(pthr_pars);
119
	   return 1;
120
	}
121
 
1 root 122
	while (1)
123
	   sleep(3600);
124
 
5 andreas 125
	pthread_exit(NULL);
1 root 126
	return 0;
127
}
128
 
129
/*
130
 * Detach application from console and make it a daemon.
131
 */
132
void daemon_start (int ignsigcld)
133
{
134
int childpid, fd;
135
 
136
        if (getpid () == 1)
137
           goto out;
138
 
139
#ifdef SIGTTOU
140
        signal (SIGTTOU, SIG_IGN);
141
#endif
142
#ifdef SIGTTIN
143
        signal (SIGTTIN, SIG_IGN);
144
#endif
145
#ifdef SIGTSTP
146
        signal (SIGTSTP, SIG_IGN);
147
#endif
148
 
149
        if ((childpid = fork ()) < 0)
150
           fprintf (stderr, "Can't fork this child\n");
151
        else if (childpid > 0)
152
           exit (0);
153
 
154
        if (setpgrp () == -1)
155
           fprintf (stderr, "Can't change process group\n");
156
 
157
        signal (SIGHUP, SIG_IGN);
158
 
159
        if ((childpid = fork ()) < 0)
160
           syslog (LOG_DAEMON, "Can't fork second child");
161
        else if (childpid > 0)
162
           exit (0);            /* first child */
163
 
164
        /* second child */
165
out:
166
        for (fd = 0; fd < NOFILE; fd++)
167
           close (fd);
168
 
169
        errno = 0;
170
        chdir ("/");
171
        umask (0);
172
 
173
        if (ignsigcld)
174
        {
175
//#ifdef SIGTSTP
176
//           signal (SIGCLD, sig_child);
177
//#else
178
           signal (SIGCLD, SIG_IGN);
179
//#endif
180
        }
181
}
182
 
183
void sig_child ()
184
{
185
#if defined(BSD) && !defined(sinix) && !defined(Linux)
186
int pid;
187
union wait status;
188
 
189
        while ((pid = wait3 (&status, WNOHANG, (struct rusage *)0)) > 0)
190
                ;
191
#endif
192
}
193
 
194
void changeToUser(char *usr, char *grp)
195
{
196
gid_t gr_gid;
197
 
198
	if (usr && strlen(usr))
199
	{
200
	   /* get uid */
201
	   struct passwd *userpwd;
202
	   struct group *usergrp;
203
 
204
	   if ((userpwd = getpwnam(usr)) == NULL)
205
	   {
206
	      syslog(LOG_DAEMON,"no such user: %s", usr);
207
	      exit(EXIT_FAILURE);
208
	   }
209
 
210
	   if (!grp || strlen(grp) || (usergrp = getgrnam(grp)) == NULL)
211
	   {
212
	      if (grp && strlen(grp))
213
	         syslog(LOG_WARNING,"no such group: %s", grp);
214
 
215
	      gr_gid = userpwd->pw_gid;
216
	   }
217
	   else
218
	      gr_gid = usergrp->gr_gid;
219
 
220
	   if (setgid(gr_gid) == -1)
221
	   {
222
	      syslog(LOG_DAEMON,"cannot setgid of user %s: %s", usr, strerror(errno));
223
	      exit(EXIT_FAILURE);
224
	   }
225
 
226
#ifdef _BSD_SOURCE
227
	   /* init suplementary groups
228
	    * (must be done before we change our uid)
229
	    */
230
	   if (initgroups(usr, gr_gid) == -1)
231
	      syslog(LOG_DAEMON,"cannot init suplementary groups of user %s: %s", usr, strerror(errno));
232
#endif
233
 
234
	   /* set uid */
235
	   if (setuid(userpwd->pw_uid) == -1)
236
	   {
237
	      syslog(LOG_DAEMON,"cannot change to uid of user %s: %s\n", usr, strerror(errno));
238
	      exit(EXIT_FAILURE);
239
	   }
240
 
241
	   if(userpwd->pw_dir)
242
	      setenv("HOME", userpwd->pw_dir, 1);
243
        }
244
}
245
 
5 andreas 246
void *pthr_temperature(void *pV_data)
247
{
248
time_t t, tstart;
249
struct tm *zeit, sz;
250
int sleep_sec;
251
 
252
	// Initialize the serial port.
253
	serial_set_method((strlen(configs.Device)) ? 1 : 0, configs.VID, configs.PID);
254
 
255
	if (strlen(configs.Device))
256
	   serial_set_device(configs.Device);
257
 
258
	if (!serial_open())
259
	   return NULL;
260
 
261
	if (serialDev.switch_fd == -1)
262
	{
263
	   if (!serial_open())
264
	      return NULL;
265
	}
266
 
267
	pthread_mutex_lock (&fastmutex_ser);
8 andreas 268
	sleep(1);	// Give the other thread time to initialize the structure
5 andreas 269
 
270
	while(1)
271
	{
272
	   HEIZINDEX *act;
273
 
8 andreas 274
	   GetTemp();
5 andreas 275
	   sleep_sec = 300;
276
 
277
	   // Compare the actual temperature with the one that should be
278
	   // and switch heating on or off
279
	   if (HeizFirst)
280
	   {
281
	      int wday;
282
	      unsigned long loctm;
283
 
284
	      t = time (NULL);
285
	      sleep_sec = 300 - (int)((t + 300L) % 300L);
286
	      sleep_sec++;
287
	      zeit = localtime (&t);
288
 
289
	      wday = (zeit->tm_wday == 0) ? 7 : zeit->tm_wday;
290
	      memset (&sz, 0, sizeof (struct tm));
291
	      sz.tm_min = 0;
292
	      sz.tm_hour = 0;
293
	      sz.tm_mon = zeit->tm_mon;
294
	      sz.tm_mday = zeit->tm_mday;
295
	      sz.tm_year = zeit->tm_year;
296
	      sz.tm_isdst = zeit->tm_isdst;
297
	      tstart = mktime(&sz);
9 andreas 298
	      loctm = (t - tstart) + 1;	// Seconds since midnight
5 andreas 299
	      act = HeizFirst;
7 andreas 300
 
5 andreas 301
	      while(act)
302
	      {
303
		 if (act->heizung->wday == wday && loctm >= act->heizung->start && loctm <= act->heizung->end)
304
		 {
305
		    if (ActTemperature == 9999.0)
306
		       SwitchOff();		// No temperature, no heating
307
		    else if (ActTemperature < 5.0)
308
		       SwitchOn();		// Make sure it will not freeze
309
		    else if (ActTemperature > 30.0)
310
		       SwitchOff();		// Don't over heat
311
		    else if (ActTemperature <= (act->heizung->temp - 0.5))
312
		       SwitchOn();
313
		    else if (ActTemperature > act->heizung->temp)
314
		       SwitchOff();
315
		 }
316
 
317
		 act = act->next;
318
	      }
319
	   }
320
	   else
321
	      syslog(LOG_INFO,"Structure not initialized!");
322
 
323
	   sleep(sleep_sec);		// Wait 5 Minutes
324
	}
325
 
326
	pthread_mutex_unlock(&fastmutex_ser);
327
}
328
 
9 andreas 329
void *processCommands(void *pV_data)
1 root 330
{
9 andreas 331
char ch, buf[128];
332
int i, s1, s;
333
struct SOCKETS *soc;
334
 
335
	soc = (struct SOCKETS *)pV_data;
336
	s1 = soc->newfd;
337
	s = soc->sockfd;
338
	memset(&buf[0], 0, sizeof(buf));
339
	i = 0;
340
 
341
	while (read(s1,&ch,1) > 0)
342
	{
343
	   if (i < (sizeof(buf) - 1))
344
	   {
345
	      buf[i] = ch;
346
 
347
	      if (ch == ';' || ch == 0x0d)
348
	      {
349
	      int pstat;
350
 
351
		 pthread_mutex_lock (&fastmutex_proc);
352
 
353
		 if (!strncmp(buf, "quit", 4))
354
		    break;
355
 
356
		 if ((pstat = parseCommand(s1,buf)) == 0)
357
		 {
358
		 char hv0[128];
359
 
360
		    sprintf(&hv0[0],"INVALID COMMAND: %s;",buf);
361
		    write(s1,hv0,strlen(hv0));
362
		 }
363
		 else if (pstat == 2)
364
		    write(s1, "NAK;", 4);
365
		 else
366
		    write(s1,"OK;",3);
367
 
368
		 memset(&buf[0], 0, sizeof(buf));
369
		 i = 0;
370
		 pthread_mutex_unlock(&fastmutex_proc);
371
		 continue;
372
	      }
373
	   }
374
 
375
	   i++;
376
	}
377
 
378
	close(s1);
379
}
380
 
381
void *pthr_parser(void *pV_data)
382
{
383
char ch, str[INET_ADDRSTRLEN];
1 root 384
// socket structure
385
struct sockaddr_in client1, server1;
386
struct servent *serviceInfo;
9 andreas 387
int s1, s;
1 root 388
socklen_t length;
9 andreas 389
struct SOCKETS soc;
1 root 390
 
391
	// socket server
392
	if (configs.port > 0)
393
	   server1.sin_port = htons(configs.port);
394
	else if ((serviceInfo = getservbyname ("heizung", "tcp")))
395
	   server1.sin_port = serviceInfo->s_port;
396
	else
397
	{
398
	   syslog(LOG_DAEMON,"Error: No TCP port defined!");
399
	   exit(EXIT_FAILURE);
400
	}
401
 
402
	server1.sin_family = AF_INET;
403
	server1.sin_addr.s_addr = INADDR_ANY;
404
	s = socket(AF_INET,SOCK_STREAM,0);
405
 
406
	if (s < 0)
7 andreas 407
	{
408
	   syslog (LOG_DAEMON, "Error in socket: %s",strerror(errno));
409
	   exit(EXIT_FAILURE);
410
	}
1 root 411
 
412
	if (bind (s, (struct sockaddr *)&server1, sizeof (server1)) < 0)
7 andreas 413
	{
414
	   syslog (LOG_DAEMON, "Error in bind: %s", strerror(errno));
415
	   exit(EXIT_FAILURE);
416
	}
1 root 417
 
418
	if (listen (s, 5) < 0)
7 andreas 419
	{
420
	   syslog (LOG_DAEMON, "Error in listen: %s", strerror(errno));
421
	   exit(EXIT_FAILURE);
422
	}
1 root 423
 
424
	length = sizeof(client1);
425
        syslog (LOG_DEBUG, "Server ready: %d",s);
426
 
427
	while (1)
428
	{
9 andreas 429
	   pthread_mutex_lock (&fastmutex);
1 root 430
	   // Read time table at every loop, to be sure to have the latest
431
	   // version (User may change it at every time)
432
	   if (!readHeizPlan())
433
	   {
434
	      if (!HeizFirst)
435
	      {
9 andreas 436
		 syslog(LOG_DAEMON, "Error reading table %s with plan!", configs.HeizPath);
437
		 exit(EXIT_FAILURE);
1 root 438
	      }
439
	      else
440
		 syslog(LOG_DAEMON, "Error reading table with plan! Will work with old one.");
441
	   }
442
 
9 andreas 443
	   pthread_mutex_unlock(&fastmutex);
444
 
1 root 445
	   if ((s1 = accept (s, (struct sockaddr *)&client1, &length)) < 0)
9 andreas 446
	   {
7 andreas 447
	      syslog (LOG_DAEMON, "Error in accept: %d: %s", s1, strerror(errno));
9 andreas 448
	      continue;
449
	   }
1 root 450
 
7 andreas 451
	   inet_ntop(AF_INET, &(client1.sin_addr), str, INET_ADDRSTRLEN);
452
	   syslog (LOG_INFO, "Connected to client %s", str);
9 andreas 453
	   soc.sockfd = s;
454
	   soc.newfd = s1;
1 root 455
 
9 andreas 456
	   if (pthread_create(&pthr_process, NULL, processCommands, (void *)&soc) != 0)
1 root 457
	   {
9 andreas 458
	      syslog (LOG_DAEMON,"Create of thread \"pthr_parser\" failed!");
459
	      close(s1);
460
	   }
461
	}
1 root 462
 
9 andreas 463
	close (s);
464
	serial_close();
465
	return NULL;
466
}
1 root 467
 
9 andreas 468
void sendList(int s1)
469
{
470
char hv0[256];
471
int i, wday = 1;
472
HEIZINDEX *act;
1 root 473
 
9 andreas 474
	// We will write out the week days in the correct order.
475
	// So we need 2 loops. The first one is runnung for every week day
476
	// and the 2nd one will find every entry for the actual week day.
477
	while (wday <= 7)
478
	{
479
	   act = HeizFirst;
480
	   i = 1;		// The line counter --> reset it to 1 for every week day
1 root 481
 
9 andreas 482
	   while (act)
483
	   {
484
	      if (act->heizung->wday == wday)
485
	      {
486
		 sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%.1f;", i, act->heizung->wday,
487
			act->heizung->start, act->heizung->end, act->heizung->temp);
488
		 write(s1, &hv0[0], strlen(hv0));
489
		 i++;
1 root 490
	      }
491
 
9 andreas 492
	      act = act->next;
1 root 493
	   }
494
 
9 andreas 495
	   wday++;
1 root 496
	}
9 andreas 497
}
1 root 498
 
9 andreas 499
int listSchemas(int s1)
500
{
501
char hv0[256];
502
DIR *dir;	
503
struct dirent *de;
504
char *ldup, *lp;
505
 
506
	if ((ldup = strdup(configs.HeizPath)) == NULL)
507
	{
508
	   syslog(LOG_DAEMON, "Not enough memory for path: %s", strerror(errno));
509
	   return 2;
510
	}
511
 
512
	lp = dirname(ldup);
513
 
514
	if ((dir = opendir(lp)) == NULL)
515
	{
516
	   syslog(LOG_DAEMON, "Could not open directory %s: %s", lp, strerror(errno));
517
	   free(ldup);
518
	   return 2;
519
	}
520
 
521
	strcpy(&hv0[0], "SCHEMA START;");
522
	write(s1, hv0, strlen(hv0));
523
 
524
	while ((de = readdir(dir)) != NULL)
525
	{
526
	   if (de->d_type == DT_REG && strstr(de->d_name, ".heat") != NULL)
527
	   {
528
	   char dn[256], *p;
529
 
530
	      memset(&dn[0], 0, sizeof(dn));
531
	      strncpy(dn, de->d_name, sizeof(dn)-1);
532
 
533
	      if ((p = strrchr(dn, '.')) != NULL)
534
		 *p = 0;
535
	      else
536
		 continue;
537
 
538
	      sprintf(&hv0[0], "SCHEMA FILE:%s;", dn);
539
	      write(s1, hv0, strlen(hv0));
540
	   }
541
	}
542
 
543
	closedir(dir);
544
	free(ldup);
545
	return 1;
1 root 546
}
547
 
548
int parseCommand(int s1, char *cmd)
549
{
4 andreas 550
char bef[32],par[1024], *p;
551
char hv0[256];
1 root 552
int i,j;
4 andreas 553
HEIZINDEX *act, *last;
1 root 554
 
555
	memset(bef,0,sizeof(bef));
556
	memset(par,0,sizeof(par));
557
 
558
	if ((p = strchr(cmd,':')) != NULL)		// do we have a parameter?
559
	{
4 andreas 560
	   strncpy(bef,cmd,p - cmd);
9 andreas 561
	   strncpy(par,p+1,strlen(p)-2);	// Cut off the trailing semi colon
1 root 562
	}
563
	else
564
	   strncpy(bef,cmd,strlen(cmd)-1);
5 andreas 565
 
4 andreas 566
	if (!strcasecmp(bef, "LIST"))		// Write out current list
9 andreas 567
	   sendList(s1);
1 root 568
 
4 andreas 569
	if (!strcasecmp(bef, "GET WDAY"))		// Write out a particular week day
1 root 570
	{
4 andreas 571
	int wday = atoi(par);
1 root 572
 
573
	   if (wday < 1 || wday > 7)
574
	   {
575
	      strcpy(&hv0[0], "ERROR:Invalid week day");
576
	      write(s1, &hv0[0], strlen(hv0));
577
	      return 0;
578
	   }
579
 
580
	   act = HeizFirst;
581
 
582
	   while (act && act->heizung->wday != wday)
583
	      act = act->next;
584
 
585
	   if (!act)
586
	   {
587
	      sprintf(&hv0[0], "ERROR:No plan for week day %d", wday);
588
	      write(s1, &hv0[0], strlen(hv0));
589
	      return 0;
590
	   }
591
 
592
	   i = 1;
593
 
594
	   while (act && act->heizung->wday == wday)
595
	   {
5 andreas 596
	      sprintf(&hv0[0], "LINE:%d:%d:%lu:%lu:%.1f;", i, act->heizung->wday,
1 root 597
		      act->heizung->start, act->heizung->end, act->heizung->temp);
598
	      write(s1, &hv0[0], strlen(hv0));
599
	      i++;
600
	      act = act->next;
601
	   }
602
	}
603
 
4 andreas 604
	if (!strcasecmp(bef, "GET TEMP"))	// Return actual temperature
1 root 605
	{
5 andreas 606
	   sprintf(&hv0[0], "TEMP:%.1f;", ActTemperature);
1 root 607
	   write(s1, &hv0[0], strlen(hv0));
608
	}
609
 
4 andreas 610
	if (!strcasecmp(bef, "GET PRESSURE"))	// Return the actual air pressure
1 root 611
	{
7 andreas 612
	   sprintf(&hv0[0], "PRESSURE:%.1f;", ActPressure);
1 root 613
	   write(s1, &hv0[0], strlen(hv0));
614
	}
615
 
8 andreas 616
        if (!strcasecmp(bef, "HEATSTAT"))	// Return the status of the heating
617
        {
618
	   sprintf(&hv0[0], "HEATSTAT:%d;", HeatStatus);
619
	   write(s1, &hv0[0], strlen(hv0));
620
	}
621
 
2 andreas 622
	// SET DAY:<count>:<day>:<end1>:<temp>[:<end2>:<temp>[:...]];
623
	// <count>   number of entries following
624
	// <day>     The day of the week
625
	// <end1>    The end time
626
	// <temp>    The temperature wanted until end time is reached
627
	//
4 andreas 628
	if (!strcasecmp(bef, "SET DAY"))		// Set the plan for a particular day
1 root 629
	{
2 andreas 630
	int count, wday, i;
631
	long endt;
632
	float temp;
633
 
4 andreas 634
	   count = atoi(par);
635
	   remove_string(par, ":", &hv0[0]);
636
	   wday = atoi(par);
637
	   freeDay(wday);
638
	   last = NULL;
2 andreas 639
 
640
	   if (count > 0)
641
	   {
642
	      for (i = 0; i < count; i++)
643
	      {
4 andreas 644
		 remove_string(par, ":", &hv0[0]);
645
		 endt = atol(par);
646
		 remove_string(par, ":", &hv0[0]);
647
		 temp = atof(par);
2 andreas 648
 
4 andreas 649
		 if ((act = allocateMemory()) == NULL)
650
		 {
651
		    syslog(LOG_DAEMON,"Error allocating memory for a temperature line.");
652
		    sprintf(&hv0[0], "ERROR:Not enough memory!");
653
		    write(s1, &hv0[0], strlen(hv0));
9 andreas 654
		    return 2;
4 andreas 655
		 }
2 andreas 656
 
4 andreas 657
		 act->heizung->wday = wday;
658
		 act->heizung->end = endt;
659
		 act->heizung->temp = temp;
660
 
661
		 if (last)
5 andreas 662
		    act->heizung->start = last->heizung->end;
4 andreas 663
		 else
664
		    act->heizung->start = 0L;
665
 
666
		 last = act;
2 andreas 667
	      }
5 andreas 668
 
669
	      writeHeizPlan();
2 andreas 670
	   }
1 root 671
	}
672
 
2 andreas 673
	// SET TEMP:<wday>:<end>:<temp>;
5 andreas 674
	if (!strcasecmp(bef, "SET TEMP"))	// Set the temperature for a particular day and line
1 root 675
	{
4 andreas 676
	int wday;
677
	unsigned long endt;
1 root 678
	float tmp;
679
 
4 andreas 680
	   wday = atoi(par);
681
	   remove_string(par, ":", &hv0[0]);
682
	   endt = atol(par);
683
	   remove_string(par, ":", &hv0[0]);
684
	   tmp = atof(par);
685
	   insertMember(wday, endt, tmp);
5 andreas 686
	   writeHeizPlan();
1 root 687
	}
688
 
9 andreas 689
	// SAVE SCHEMA:<name>;
690
	if (!strcasecmp(bef, "SAVE SCHEMA"))	// Save the current heating plan into a file
691
        {
692
	   sprintf(&hv0[0], "%s.heat", par);
693
 
694
	   if (!writeHeizPlanName(hv0))
695
	      return 2;
696
	}
697
 
698
	// LOAD SCHEMA:<name>;
699
	if (!strcasecmp(bef, "LOAD SCHEMA"))	// Load a previous saved schema from a file
700
	{
701
	   sprintf(&hv0[0], "%s.heat", par);
702
 
703
	   if (!readHeizPlanName(hv0))
704
	      return 2;
705
 
706
	   // write the new schema immediately to the default file
707
	   writeHeizPlan();
708
	   sendList(s1);
709
	}
710
 
711
	// DELETE SCHEMA:<name>;
712
	if (!strcasecmp(bef, "DELETE SCHEMA"))	// Delete an existing schema
713
        {
714
	   makeFileName(&hv0[0], par, sizeof(hv0));
715
 
716
	   if (strlen(hv0) < (sizeof(hv0) - 6))
717
	      strcat(&hv0[0], ".heat");
718
	   else
719
	   {
720
	      syslog(LOG_DAEMON, "File name too large for internal buffer");
721
	      return 2;
722
	   }
723
 
724
	   if (access(hv0, R_OK | W_OK))
725
	   {
726
	      syslog(LOG_DAEMON,"No access to file %s: %s", hv0, strerror(errno));
727
	      return 2;
728
	   }
729
 
730
	   if (unlink(hv0) == -1)
731
	   {
732
	      syslog(LOG_DAEMON,"Error deleting file %s: %s", hv0, strerror(errno));
733
	      return 2;
734
	   }
735
 
736
	   return listSchemas(s1);
737
	}
738
 
739
	// LIST SCHEMA;
740
	if (!strcasecmp(bef, "LIST SCHEMA"))	// Return a list of all available schamas
741
	   return listSchemas(s1);
742
 
1 root 743
	return 1;
744
}
745
 
746
/*
2 andreas 747
 * Remove a complete day
748
 */
749
void freeDay(int wday)
750
{
751
HEIZINDEX *act, *next, *prev;
752
 
753
	act = HeizFirst;
754
	prev = NULL;
755
 
756
	while(act)
757
	{
758
	   if (act->heizung->wday == wday)
759
	      act = freeChainMember(act);
760
 
761
	   act = act->next;
762
	}  
763
}
764
 
765
/*
766
 * Insert a new entry
767
 */
768
void insertMember(int wday, long endt, float temp)
769
{
770
HEIZINDEX *act;
771
 
772
	act = HeizFirst;
773
 
774
	while(act)
775
	{
776
	   if (act->heizung->wday == wday && act->heizung->end == endt)
777
	   {
778
	      act->heizung->temp = temp;
779
	      return;
780
	   }
781
 
782
	   act = act->next;
783
	}
784
 
785
	if ((act = allocateMemory()) != NULL)
786
	{
787
	   act->heizung->wday = wday;
788
	   act->heizung->end = endt;
789
	   act->heizung->temp = temp;
790
	}
791
}
792
 
793
/*
1 root 794
 * Free the memory for the actual heizung plan.
795
 */
796
void freeMemory()
797
{
798
HEIZINDEX *act, *next;
799
 
800
	act = HeizFirst;
801
 
802
	while(act)
803
	{
804
	   if (act->heizung)
805
	   {
806
	      free(act->heizung);
807
	      act->heizung = NULL;
808
	   }
809
 
810
	   next = act->next;
811
	   free(act);
812
	   act = next;
813
	}
5 andreas 814
 
815
	HeizFirst = NULL;
1 root 816
}
817
 
818
/*
819
 * Free only one entry in the chain
820
 */
2 andreas 821
HEIZINDEX *freeChainMember(HEIZINDEX *member)
1 root 822
{
823
HEIZINDEX *act,*prev;
824
 
825
	act = HeizFirst;
826
	prev = NULL;
827
 
828
	while(act != member)
829
	{
830
	   prev = act;
831
	   act = act->next;
832
	}
833
 
834
	if (act == member)
835
	{
836
	   if (prev)
2 andreas 837
	      prev->next = act->next;
4 andreas 838
	   else
839
	   {
840
	      prev = act->next;
1 root 841
 
4 andreas 842
	      if (act == HeizFirst)
843
		 HeizFirst = act->next;
844
	   }
845
 
1 root 846
	   if (act->heizung)
847
	      free(act->heizung);
848
 
849
	   free(act);
4 andreas 850
	   return prev;
1 root 851
	}
2 andreas 852
 
853
	return NULL;
1 root 854
}
855
 
856
/*
4 andreas 857
 * Allocate the memory for the actual heizung plan.
858
 * This function appends an element to the end of the chain.
1 root 859
 */
860
HEIZINDEX *allocateMemory()
861
{
862
HEIZINDEX *act, *last;
863
 
864
	if (!HeizFirst)
865
	{
866
	   HeizFirst = malloc(sizeof(HEIZINDEX));
867
 
868
	   if (HeizFirst)
869
	   {
870
	      HeizFirst->heizung = malloc(sizeof(HEIZUNG));
871
	      HeizFirst->next = NULL;
872
	   }
873
	   else
874
	      return NULL;
875
 
876
	   return HeizFirst;
877
	}
878
	else
879
	{
880
	   // Find last element
881
	   last = HeizFirst;
882
 
883
	   while(last->next)
884
	      last = last->next;
885
 
886
	   act = malloc(sizeof(HEIZINDEX));
887
 
888
	   if (act)
889
	   {
890
	      act->heizung = malloc(sizeof(HEIZUNG));
891
	      act->next = NULL;
892
	      last->next = act;
893
	   }
894
	   else
895
	      return NULL;
896
 
897
	   return act;
898
	}
899
 
900
	return NULL;
901
}
902
 
903
/*
4 andreas 904
 * Allocate the memory for the actual heizung plan.
905
 * This function inserts an element into the chain.
906
 */
907
HEIZINDEX *insertMemory(HEIZINDEX *pos)
908
{
909
HEIZINDEX *act;
910
 
911
	if (!HeizFirst)
912
	{
913
	   HeizFirst = malloc(sizeof(HEIZINDEX));
914
 
915
	   if (HeizFirst)
916
	   {
917
	      HeizFirst->heizung = malloc(sizeof(HEIZUNG));
918
	      HeizFirst->next = NULL;
919
	   }
920
	   else
921
	      return NULL;
922
 
923
	   return HeizFirst;
924
	}
925
	else
926
	{
927
	   act = malloc(sizeof(HEIZINDEX));
928
 
929
	   if (act)
930
	   {
931
	      act->heizung = malloc(sizeof(HEIZUNG));
932
	      act->next = pos->next;
933
	      pos->next = act;
934
	   }
935
	   else
936
	      return NULL;
937
 
938
	   return act;
939
	}
940
 
941
	return NULL;
942
}
943
 
944
/*
1 root 945
 * The following functions read a config file and put the
946
 * contents into a structure.
947
 */
948
void readConf(void)
949
{
950
int fd;
951
char confFile[512], line[512];
952
char *home, *p;
953
char hv0[64], hv1[128];
954
 
955
	home = getenv("HOME");
956
	fd = -1;
957
 
958
	if (!access("/etc/heizung.conf",R_OK))
959
	   strcpy(confFile,"/etc/heizung.conf");
960
	else if (!access("/etc/heizung/heizung.conf",R_OK))
961
	   strcpy(confFile,"/etc/heizung/heizung.conf");
962
	else if (!access("/usr/etc/heizung.conf",R_OK))
963
	   strcpy(confFile,"/usr/etc/heizung.conf");
964
	else if (home)
965
	{
966
	   strcpy(confFile,home);
967
	   strcat(confFile,"/.heizung.conf");
968
 
969
	   if (access(confFile,R_OK))
4 andreas 970
	   {
971
	      syslog(LOG_WARNING,"Even config file %s was not found!", confFile);
1 root 972
	      confFile[0] = 0;
4 andreas 973
	   }
1 root 974
	}
975
	else
976
	   confFile[0] = 0;
977
 
5 andreas 978
	memset(&configs, 0, sizeof(configs));
979
	strcpy(configs.User,"nobody");
980
	strcpy(configs.Grp,"nobody");
981
	strcpy(configs.HeizPath, "/var/www/.HeizPlan.conf");
982
	strcpy(configs.Werte, "/var/log/werte.dat");
983
	strcpy(configs.Device, "/dev/ttyS1");
984
	configs.port = 11001;
1 root 985
 
4 andreas 986
	if (!confFile[0] || (fd = open(confFile,O_RDONLY)) == -1)
1 root 987
	{
4 andreas 988
	   if (confFile[0])
989
	      syslog(LOG_WARNING,"Error opening the config file %s! Using built in defaults. (%s)", confFile, strerror(errno));
990
	   else
991
	      syslog(LOG_WARNING,"Error opening the config file! Using built in defaults.");
992
 
1 root 993
	   return;
994
	}
995
 
4 andreas 996
	while (readLine(fd, &line[0], sizeof(line)) != NULL)
1 root 997
	{
998
	   int len;
999
 
1000
	   trim (&line[0]);
1001
 
4 andreas 1002
	   if (line[0] == '#' || !strlen(line))
1 root 1003
	      continue;
1004
 
1005
	   if ((p = strchr (line, '=')) == NULL)
1006
	      continue;
1007
 
1008
	   *p = 0;
1009
	   p++;
1010
	   len = strlen(line);
1011
 
1012
	   if (len > sizeof(hv0))
1013
	      len = sizeof(hv0) - 1;
1014
 
1015
	   strncpy (hv0, line, len);
4 andreas 1016
	   hv0[len] = 0;
1 root 1017
	   trim (hv0);
1018
	   len = strlen(p);
1019
 
1020
	   if (len > sizeof(hv1))
1021
	      len = sizeof(hv0) - 1;
1022
 
1023
	   strncpy (hv1, p, len);
4 andreas 1024
	   hv1[len] = 0;
1 root 1025
	   trim (hv1);
1026
 
1027
	   if (!strcasecmp(hv0, "user"))
4 andreas 1028
	   {
1029
	      syslog(LOG_INFO,"Found \"user\": %s", hv1);
1 root 1030
	      strncpy (configs.User, hv1, sizeof(configs.User));
4 andreas 1031
	   }
1 root 1032
 
1033
	   if (!strcasecmp(hv0, "group"))
4 andreas 1034
	   {
1035
	      syslog(LOG_INFO,"Found \"group\": %s", hv1);
1 root 1036
	      strncpy (configs.Grp, hv1, sizeof(configs.Grp));
4 andreas 1037
	   }
1 root 1038
 
1039
	   if (!strcasecmp(hv0, "port"))
4 andreas 1040
	   {
1041
	      syslog(LOG_INFO,"Found \"port\": %s", hv1);
1 root 1042
	      configs.port = atoi (hv1);
4 andreas 1043
	   }
1 root 1044
 
1045
	   if (!strcasecmp(hv0, "heizpath"))
4 andreas 1046
	   {
1047
	      syslog(LOG_INFO,"Found \"heizpath\": %s", hv1);
1 root 1048
	      strncpy (configs.HeizPath, hv1, sizeof(configs.HeizPath));
4 andreas 1049
	   }
5 andreas 1050
 
1051
	   if (!strcasecmp(hv0, "Werte"))
1052
	   {
1053
	      syslog(LOG_INFO,"Found \"Werte\": %s", hv1);
1054
	      strncpy (configs.Werte, hv1, sizeof(configs.Werte));
1055
	   }
1056
 
1057
	   if (!strcasecmp(hv0, "Device"))
1058
	   {
1059
	      syslog(LOG_INFO,"Found \"Device\": %s", hv1);
1060
	      strncpy (configs.Device, hv1, sizeof(configs.Device));
1061
	   }
1062
 
1063
	   if (!strcasecmp(hv0, "VID"))
1064
	   {
1065
	      syslog(LOG_INFO,"Found VendorID: %04x", atoi(hv1));
1066
	      configs.VID = atoi(hv1);
1067
	   }
1068
 
1069
	   if (!strcasecmp(hv0, "PID"))
1070
	   {
1071
	      syslog(LOG_INFO,"Found ProductID: %04x", atoi(hv1));
1072
	      configs.PID = atoi(hv1);
1073
	   }
1 root 1074
	}
1075
 
1076
	close (fd);
1077
}
1078
 
9 andreas 1079
char *makeFileName(char *ret, char *fname, int len)
1080
{
1081
char *fdup, *pright, *cfg, *lcfg;
1082
 
1083
	if (ret == NULL || fname == NULL || !len || !strlen(fname))
1084
	   return NULL;
1085
 
1086
	if (!access(fname, R_OK | W_OK))
1087
	{
1088
	   memset(ret, 0, len);
1089
	   strncpy(ret, fname, len-1);
1090
	   return ret;
1091
	}
1092
 
1093
	fdup = strdup(fname);
1094
 
1095
	if (fdup == NULL)
1096
	{
1097
	   syslog(LOG_DAEMON, "Error allocating memory: %s", strerror(errno));
1098
	   return NULL;
1099
	}
1100
 
1101
	cfg = strdup(configs.HeizPath);
1102
 
1103
	if (cfg == NULL)
1104
	{
1105
	   syslog(LOG_DAEMON, "Error allocating memory: %s", strerror(errno));
1106
	   free(fdup);
1107
	   return NULL;
1108
	}
1109
 
1110
	pright = basename(fdup);
1111
	lcfg = dirname(cfg);
1112
	memset(ret, 0, len);
1113
	strncpy(ret, lcfg, len-1);
1114
 
1115
	if (strlen(ret) < (len - (strlen(pright) + 2)))
1116
	{
1117
	   strcat(ret, "/");
1118
	   strcat(ret, pright);
1119
	   free(fdup);
1120
	   free(cfg);
1121
	   return ret;
1122
	}
1123
 
1124
	free(fdup);
1125
	free(cfg);
1126
	return NULL;
1127
}
1128
 
1 root 1129
int readHeizPlan(void)
1130
{
9 andreas 1131
	return readHeizPlanName(configs.HeizPath);
1132
}
1133
 
1134
int readHeizPlanName(char* fname)
1135
{
1 root 1136
int fd, i, wday;
1137
ulong tim;
1138
char line[512];
1139
char *p, *xp;
1140
char hv0[64], hv1[128];
9 andreas 1141
char path[512];
1 root 1142
float temperature;
7 andreas 1143
int counter = 0;
4 andreas 1144
HEIZINDEX *act, *prev;
1 root 1145
 
1146
	fd = -1;
1147
 
9 andreas 1148
	if (makeFileName(&path[0], fname, sizeof(path)) == NULL)
4 andreas 1149
	{
9 andreas 1150
	   syslog(LOG_DAEMON,"No or invalid path %s", fname);
1 root 1151
	   return 0;
4 andreas 1152
	}
1 root 1153
 
9 andreas 1154
	if (access(path, R_OK))
4 andreas 1155
	{
9 andreas 1156
	   syslog(LOG_DAEMON,"Access to file %s denied: %s", path, strerror(errno));
1 root 1157
	   return 0;
4 andreas 1158
	}
1 root 1159
 
9 andreas 1160
	if ((fd = open(path, O_RDONLY)) == -1)
1161
	{
1162
	   syslog(LOG_DAEMON,"Error opening file %s: %s", path, strerror(errno));
1163
	   return 0;
1164
	}
1165
 
4 andreas 1166
	act = prev = NULL;
1167
	freeMemory();
1168
 
1169
	while (readLine(fd, &line[0], sizeof(line)) != NULL)
1 root 1170
	{
1171
	   int len;
1172
 
1173
	   trim (&line[0]);
1174
 
5 andreas 1175
	   if (line[0] == '#' || !strlen(line) || strchr(line, ',') == NULL)
1 root 1176
	      continue;
1177
 
4 andreas 1178
	   // We need a place to store the information
1179
	   prev = act;
1180
 
1181
	   if ((act = allocateMemory()) == NULL)
1182
	   {
1183
	      close(fd);
1184
	      syslog(LOG_DAEMON,"Error allocating memory for a temperature line! Stopped reading file %s.", configs.HeizPath);
1185
	      return 0;
1186
	   }
1187
 
7 andreas 1188
	   counter++;
4 andreas 1189
	   memset(act->heizung, 0, sizeof(HEIZUNG));
1 root 1190
	   // Parse a line. The line has the format:
1191
	   // <wday>,<end>,<temperature>
4 andreas 1192
	   p = strtok(line, ",");
1 root 1193
	   i = 1;
1194
 
1195
	   while(p)
1196
	   {
1197
	      switch(i)
1198
	      {
1199
		 case 1:	// Week day
1200
		    wday = atoi(p);
1201
 
1202
		    if (wday < 1 || wday > 7)	// valid line?
1203
		    {
4 andreas 1204
		       if (prev)
1205
			  wday = prev->heizung->wday;
1 root 1206
 
4 andreas 1207
		       if (wday < 1 || wday > 7)
1208
		       {
1209
			  p = strtok(NULL, ",");
1210
			  i++;
1211
			  continue;
1212
		       }
1 root 1213
		    }
1214
 
1215
		    act->heizung->wday = wday;
1216
		    act->heizung->start = 0;
1217
		 break;
1218
 
1219
		 case 2:	// start/end time
1220
		    if ((xp = strchr(p, ':')) != NULL)
1221
		    {
1222
		    int hour, min;
1223
 
1224
		       hour = atoi(p);
1225
		       min = atoi(xp+1);
1226
 
1227
		       if (hour >= 0 && hour <= 23 && min >= 0 && min <= 59)
1228
		       {
5 andreas 1229
			  act->heizung->end = hour * 3600 + min * 60 + 59;
1 root 1230
 
4 andreas 1231
			  if (prev && prev->heizung->wday == act->heizung->wday)
1232
			     act->heizung->start = prev->heizung->end;
1 root 1233
		       }
1234
		    }
1235
		 break;
1236
 
1237
		 case 3:	// temperature
1238
		    temperature = atof(p);
1239
 
1240
		    if (temperature < 5.0 || temperature > 30.0)
1241
		    {
4 andreas 1242
		       p = strtok(NULL,",");
1243
		       i++;
1 root 1244
		       continue;
1245
		    }
1246
 
1247
		    act->heizung->temp = temperature;
1248
		 break;
1249
	      }
1250
 
4 andreas 1251
	      p = strtok(NULL, ",");
1252
	      i++;
1 root 1253
	   }
1254
	}
1255
 
7 andreas 1256
	syslog(LOG_INFO,"Found %d entries in %s", counter, configs.HeizPath);
1 root 1257
	close (fd);
1258
	return 1;
1259
}
1260
 
1261
/*
1262
 * write the (may be) altered plan.
1263
 * This function allways writes whole plan.
1264
 */
1265
int writeHeizPlan()
1266
{
9 andreas 1267
	return writeHeizPlanName(configs.HeizPath);
1268
}
1269
 
1270
int writeHeizPlanName(char *fname)
1271
{
1 root 1272
int fd;
9 andreas 1273
char hv0[512], path[512];
1 root 1274
HEIZINDEX *act;
1275
 
1276
	fd = -1;
1277
 
9 andreas 1278
	if (makeFileName(&path[0], fname, sizeof(path)) == NULL)
1279
	{
1280
	   syslog(LOG_DAEMON,"No or invalid path %s", fname);
1 root 1281
	   return 0;
9 andreas 1282
	}
1 root 1283
 
9 andreas 1284
	if ((fd = open(path, O_RDWR | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
1285
	{
1286
	   syslog(LOG_DAEMON, "Error opening/creating file %s: %s", path, strerror(errno));
1 root 1287
	   return 0;
9 andreas 1288
	}
1 root 1289
 
1290
	act = HeizFirst;
1291
 
1292
	while(act)
1293
	{
5 andreas 1294
	   if (act->heizung->wday)
1295
	   {
1296
	      int hour, min;
1297
 
1298
	      hour = act->heizung->end / 3600;
1299
	      min = (act->heizung->end - (hour * 3600)) / 60;
1300
 
1301
	      if (hour > 23)
1302
	      {
1303
		 hour = 23;
1304
		 min = 59;
1305
	      }
1306
 
1307
	      sprintf(&hv0[0], "%d,%02d:%02d,%.1f\n",
1308
		   act->heizung->wday, hour, min,
1 root 1309
		   act->heizung->temp);
5 andreas 1310
	      write(fd, &hv0[0], strlen(hv0));
1311
	   }
1312
 
1 root 1313
	   act = act->next;
1314
	}
1315
 
1316
	close(fd);
1317
	return 1;
1318
}
1319
 
4 andreas 1320
char *readLine(int fd, char *buf, int bufLen)
1 root 1321
{
4 andreas 1322
int i, end;
1 root 1323
char ch, *p;
1324
 
1325
        if (fd <= 0)
4 andreas 1326
	{
1327
	   syslog(LOG_DAEMON,"Function readLine was called with an invalid file descriptor of %d!", fd);
1328
           return NULL;
1329
	}
1 root 1330
 
4 andreas 1331
        i = end = 0;
1332
        p = buf;
1 root 1333
 
4 andreas 1334
        while (read(fd, &ch, 1) > 0)
1335
        {
1336
           end = 1;
1 root 1337
 
4 andreas 1338
           if (ch == 0x0a)
1339
           {
1340
             *p = 0;
1341
             return buf;
1342
           }
1 root 1343
 
4 andreas 1344
           if (ch == 0x0d)      // ignore this!
1345
              continue;
1 root 1346
 
4 andreas 1347
           if (i < (bufLen - 1))
1348
           {
1349
              *p = ch;
1350
              p++;
1351
              i++;
1352
           }
1353
        }
1 root 1354
 
4 andreas 1355
        *p = 0;
1356
 
1357
        if (end)
1358
           return buf;
1359
        else
1360
           return NULL;
1 root 1361
}
1362
 
1363
char *trim(char *str)
1364
{
1365
char *p1, *p2, *p;
1366
 
1367
	if (!str)
1368
	   return NULL;
1369
 
4 andreas 1370
	if (!strlen(str))
1371
	   return str;
1372
 
1 root 1373
	p = str;
1374
	p1 = p2 = NULL;
1375
 
1376
	while (*p)
1377
	{
1378
	   if (!p1 && *p != ' ')
1379
	   {
1380
	      p1 = p;
1381
	      break;
1382
	   }
1383
 
1384
	   p++;
1385
	}
1386
 
1387
	p2 = str + (strlen(str) - 1);
1388
 
1389
	while (p2 > str && *p2 == ' ')
1390
	   p2--;
1391
 
1392
	if (p2)
1393
	   *(p2+1) = 0;
1394
 
1395
	if (p1)
1396
	{
1397
	   char *buf = strdup (p1);
1398
	   strcpy (str, buf);
1399
	   free (buf);
1400
	}
1401
 
1402
	return str;
1403
}
2 andreas 1404
 
1405
char *remove_string(char *str, char *search, char *ret)
1406
{
1407
char *p;
1408
 
1409
	if (!strlen(str) || !strlen(search))
1410
	   return NULL;
1411
 
1412
	if ((p = strstr(str, search)) != NULL)
1413
	{
1414
	int len = strlen(search);
1415
 
4 andreas 1416
	   strncpy(ret, str, p - str + len);
5 andreas 1417
	   ret[p - str + len] = 0;
1418
	   memmove(str, p + len, strlen(p+len));
1419
	   str[strlen(p+len)] = 0;
2 andreas 1420
	   return ret;
1421
	}
1422
 
1423
	return NULL;
4 andreas 1424
}